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

时间:2017-10-19 17:11:33

标签: c# .net json unit-testing json.net

写了一个Custom JsonConverter来处理由同一个api的不同版本返回的不同Json格式。一个应用程序向其他几个应用程序发出请求,我们不知道将返回哪种格式,因此JsonConverter处理这个并且似乎运行良好。我需要在项目中添加单元测试,除了我没有找到有用的资源来帮助模拟一些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);
    }

一些代码已从ReadJson方法中取出。我正在尝试安装JsonReader来返回实际json的值,在这种情况下是一个空值,但在其他单元测试中我想要一个实际的Json(JObject)。运行单元测试时,我收到一个" Newtonsoft.JsonReaderException:从JsonReader读取JObject时出错。路径''。"

5 个答案:

答案 0 :(得分:5)

DeserializeObject<T>的使用会在你的引擎下调用你的ReadJson。

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

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

    Assert.IsNotNull(resultCheck);
}

答案 1 :(得分:0)

在不知道自定义转换器的内部工作原理的情况下,您是否可以创建一个测试双精度而不是模拟?

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

    public override object Value => null;
}

答案 2 :(得分:0)

要测试转换器,它必须在转换器列表中。如果您使用了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);

答案 3 :(得分:0)

尝试使用它来检查转换结果。

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

From Documentation

答案 4 :(得分:0)

虽然直接使用JsonConvertJsonSerializer可以进行测试,但您可能应该使转换器测试更加直接。例如,您不能保证在调用反序列化器时JSON.NET会按预期执行操作,而实际上想要要测试的是您的自定义转换器-JSON.NET所做的就是失去控制。

考虑以下示例:

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; }
}

我有这个结构,它由int支持。我想将特定的JSON number值反序列化为int-> UserId。因此,我创建了一个自定义转换器:

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);
  }
}
  

在这种情况下,我跳过了WriteJson的实现,但是逻辑是相同的。

我将如下编写测试:

[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));

这样做,我可以完全根据我的ReadJson方法创建一个测试,并确认它是否符合我的期望。 更进一步,我可能会模拟JsonReaderJsonSerializer之类的元素来导致不同的前提条件,因此我可以测试各种场景。

依靠JsonConvertJsonSerializer来运行完整的反序列化过程的问题是,您正在引入其他很大程度上无法控制的逻辑。也就是说,如果通过反序列化,JSON.NET实际上做出了不同的决定,而您的自定义转换器从未使用过-您的测试不负责测试JSON.NET本身,而是自定义转换器实际执行的操作。