简体   繁体   English

Mock JsonReader单元如何测试自定义JsonConverter

[英]How Mock JsonReader unit testing a custom JsonConverter

Wrote a Custom JsonConverter to handle different Json formats that are returned by different versions of the same api. 写了一个Custom JsonConverter来处理由同一个api的不同版本返回的不同Json格式。 One app makes a request to several other apps, and we dont know which format will be returned so the JsonConverter handles this and seems to work well. 一个应用程序向其他几个应用程序发出请求,我们不知道将返回哪种格式,因此JsonConverter处理这个并且似乎运行良好。 I need to add unit tests to the project, except I have not found helpful resources to help Mock out some of the Newtonsoft.Json objects, mainly JsonReader. 我需要在项目中添加单元测试,除了我没有找到有用的资源来帮助模拟一些Newtonsoft.Json对象,主要是JsonReader。

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jsonValue = JObject.Load(reader);
        if(jsonValue == null)
        {
            return null;
        }

        var responseData = ReadJsonObject(jsonValue);
        return responseData;
    }

    [TestMethod]
    public void ReadJsonReturnNullForNullJson()
    {
        var converter = new DataConverter();

        _mockJsonReader.Setup(x => x.Value).Returns(null);

        var responseData = converter.ReadJson(_mockJsonReader.Object, typeof(ProbeResponseData), null, _mockJsonSerializer.Object);

        Assert.IsNull(responseData);
    }

Some code has been taken out of the ReadJson method. 一些代码已经从ReadJson方法中取出。 I am trying Setup the JsonReader to return the value of the actual json, in this case a null value but in other unit tests I would want an actual Json(JObject). 我正在尝试安装JsonReader来返回实际json的值,在这种情况下是一个空值,但在其他单元测试中我想要一个实际的Json(JObject)。 When running the unit test I receive a "Newtonsoft.JsonReaderException: Error reading JObject from JsonReader. Path ''." 运行单元测试时,我收到一个“Newtonsoft.JsonReaderException:从JsonReader读取JObject时出错。路径''。”

The use of DeserializeObject<T> will call your override of ReadJson under the hood. 使用DeserializeObject<T>将调用您的ReadJson覆盖。

[TestMethod]
public void ReadJsonVerifyTypeReturned()
{
    var testJson = CreateJsonString();

    var result = JsonConvert.DeserializeObject<ProbeResponseData>(testJson);
    var resultCheck = result as ProbeResponseData;

    Assert.IsNotNull(resultCheck);
}

Without knowing the inner workings of your custom converter, could you just create a test double instead of a mock? 在不知道自定义转换器的内部工作原理的情况下,您是否可以创建一个测试双重而不是模拟?

public class JsonReaderThatReturnsNull : JsonReader
{
    public override bool Read()
    {
        return true;
    }

    public override object Value => null;
}

To get to test the converter, it needs to be in the list of converters. 要测试转换器,它需要在转换器列表中。 If you used JsonConverterAttribute , it will work automatigically, but if you didn't you can do it like this: 如果您使用了JsonConverterAttribute ,它将自动运行,但如果您没有,您可以这样做:

var serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new MyJsonConverter());

var serializer = JsonSerializer.Create(serializerSettings);

var jObject = JObject.Parse(myString);
var result = jObject.ToObject<MyObject>(serializer);

Try to use this to check converted result. 尝试使用它来检查转换结果。

DeserializedType result = JsonConvert.DeserializeObject<DeserializedType>(json, new Converter(parms));

From Documentation 来自文档

Whilst using JsonConvert or JsonSerializer directly will allow you to test it, you probably should make your converter tests a little more direct. 虽然直接使用JsonConvertJsonSerializer可以让你测试它,但你可能应该让你的转换器测试更直接。 For instance, you can't guarantee that JSON.NET will do what you expect when you call the deserializer, whereas what you actually want to test is your custom converter - what JSON.NET does with that is out of your control. 例如,您无法保证JSON.NET在调用反序列化器时会执行您期望的操作,而您实际想要测试的是您的自定义转换器 - JSON.NET的功能是您无法控制的。

Consider this example: 考虑这个例子:

public readonly struct UserId
{
  public static readonly UserId Empty = new UserId();

  public UserId(int value)
  {
    Value = value;
    HasValue = true;
  }

  public int Value { get; }
  public bool HasValue { get; }
}

I've got this struct, which is backed by an int . 我有这个结构,由int支持。 I want to deserialize a specific JSON number value as an int -> UserId . 我想反序列化JSON特定number值作为int - > UserId So, I create a custom converter: 所以,我创建了一个自定义转换器:

public class UserIdConverter
{
  public override bool CanConvert(Type objectType) => objectType == typeof(UserId);

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
    int? id = serializer.Deserialize<int?>(reader);
    if (!id.HasValue)
    {
      return UserId.Empty;
    }

    return new UserId(id.Value);
  }
}

I've skipped over the implementation of WriteJson in this instance, but the logic is the same. 我在这个例子中跳过了WriteJson的实现,但逻辑是一样的。

I would write my test as follows: 我会按如下方式编写测试:

[Fact]
public void UserIdJsonConverter_CanConvertFromJsonNumber()
{
    // Arrange
    var serialiser = new JsonSerializer();
    var reader = CreateJsonReader("10");
    var converter = new UserIdJsonConverter();

    // Act
    var result = converter.ReadJson(reader, typeof(UserId), null, serialiser);

    // Assert
    Assert.NotNull(result);
    Assert.IsType<UserId>(result);

    var id = (UserId)result;
    Assert.True(id.HasValue);
    Assert.Equal(10, id.Value);
}

private JsonTextReader CreateJsonReader(string json)
    => new JsonTextReader(new StringReader(json));

In doing so, I can create a test purely around my ReadJson method, and confirm it does what I expect. 这样做,我可以完全围绕我的ReadJson方法创建一个测试,并确认它符合我的预期。 Going further , I could potentially mock elements, such as the JsonReader and JsonSerializer to result in different preconditions so I can test a wide array of scenarios. 更进一步 ,我可能会模拟元素,例如JsonReaderJsonSerializer从而产生不同的前提条件,因此我可以测试各种各样的场景。

The issue with relying on JsonConvert or JsonSerializer to run the full deserialization process, is that you're introducing other logic which is largely outside of your control. 依赖于JsonConvertJsonSerializer来运行完整反序列化过程的问题在于,您正在引入其他逻辑,这些逻辑在很大程度上是您无法控制的。 Ie, what if through deserialization, JSON.NET actually makes a different decision and your custom converter is never used - your test isn't responsible for testing JSON.NET itself, but what your custom converter actually does. 即,如果通过反序列化,JSON.NET实际上做出了不同的决定并且您的自定义转换器从未使用过 - 您的测试不负责测试JSON.NET本身,而是您的自定义转换器实际执行的操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM