如何对自定义 JsonConverter 进行单元测试

2024-01-18

我有一个 json 负载,我想以一种不平凡的方式反序列化。

{
   "destinationId": 123
}

目标类是

public class SomeObject
{
    public Destination Destination { get; set; }
}

public class Destination
{
    public Destination(int destinationId)
    {
        Id = destinationId;
    }

    public int Id { get; set; }
}

为了能够做到这一点,我创建了一个JsonConverter就可以了。

这是 ReadJson 方法:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (CanConvert(objectType))
    {
        var value = reader.Value;

        if (value is long v)
        {
            // TODO: this might overflow
            return new Destination((int)v);
        }
    }

    return null;
}

然后我用一个装饰 Destination 类[JsonConverter]属性接受一个typeof(DestinationConverter).

当我使用时这可以正常工作JsonConvert.DeserializeObject<SomeObject>(myString)(请参阅下面的单元测试)但我在为以下内容创建成功的单元测试时遇到问题JsonConverter具体来说(参见下面的第二个测试)。

[Test, AutoData]
public void SomeObject_is_correctly_deserialized(SomeObject testObject)
{
    var json = $@"{{""destinationId"":{testObject.Destination.Id}}}";

    Console.WriteLine($"json: {json}");

    var obj = JsonConvert.DeserializeObject<SomeObject>(json);

    Assert.That(obj.Destination.Id, Is.EqualTo(testObject.Destination.Id));
}

[Test, AutoData]
public void ReadJson_can_deserialize_an_integer_as_Destination(DestinationConverter sut, int testValue)
{
    JsonReader reader = new JTokenReader(JToken.Parse($"{testValue}"));

    var obj = sut.ReadJson(reader, typeof(Destination), null, JsonSerializer.CreateDefault());

    var result = obj as Destination;

    Assert.That(result, Is.Not.Null);
    Assert.That(result, Is.InstanceOf<Destination>());
    Assert.That(result.Id, Is.EqualTo(testValue));
}

我一直在谷歌上搜索一种正确对转换后的单元进行测试的方法,但我只找到了人们使用整个DeserializeObject而不仅仅是测试转换器。

PS:我将所有必要的代码粘贴到 .NET Fiddle 中:https://dotnetfiddle.net/oUXi6k https://dotnetfiddle.net/oUXi6k


你的基本问题是,当你创建一个JsonReader, 其初始定位为before第一个令牌。这在的文档JsonToken https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonToken.htm:

JsonToken 枚举

指定 JSON 令牌的类型。

Members

  • None: 0如果尚未调用读取方法,则由 JsonReader 返回此值。

因此,为了正确地对转换器进行单元测试,您需要将读取器前进到您尝试读取的 c# 对象的第一个标记,例如像这样:

JsonReader reader = new JsonTextReader(new StringReader(json));
while (reader.TokenType == JsonToken.None)
    if (!reader.Read())
        break;

var obj = sut.ReadJson(reader, typeof(Destination), null, JsonSerializer.CreateDefault());

小提琴样本here https://dotnetfiddle.net/IEhVv9.

完成此操作后,我建议您按如下方式重写转换器:

public class DestinationConverter : JsonConverter
{
    public override bool CanConvert(System.Type objectType)
    {
        return objectType == typeof(Destination);
    }

    public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
    {
        var id = serializer.Deserialize<int?>(reader);
        if (id == null)
            return null;
        return new Destination(id.Value);
    }

    public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
    {
        // WriteJson() is never called with a null value, instead Json.NET writes null automatically.
        writer.WriteValue(((Destination)value).Id);
    }
}

通过致电serializer.Deserialize<int?>(reader) inside ReadJson(),您保证:

  • null值在读取期间进行处理。

  • 如果 JSON 格式不正确(例如截断的文件),则会抛出异常。

  • 如果 JSON 无效(例如,需要整数的对象或整数溢出),则会引发异常。

  • 读取器将正确定位在正在读取的令牌的末尾。 (如果令牌是原始令牌,则读者不需要高级,但对于更复杂的令牌,则需要高级。)

小提琴样本#2here https://dotnetfiddle.net/SFT44r.

您可能还想增强单元测试以检查:

  1. 阅读器正确定位后ReadJson(),例如通过断言TokenType https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonReader_TokenType.htm and Depth https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonReader_Depth.htm是正确的,或者甚至计算 JSON 流中剩余的令牌数量并断言它符合预期。

    编写转换器时的一个常见错误是在转换后让读者定位错误。完成此操作后,该对象itself读取成功但是全部随后的对象变得腐败。单元测试ReadJson()除非您断言读者随后已正确定位,否则直接将无法捕获此内容。

  2. 对于格式不正确的 JSON 流会引发异常,例如被截断的一个。

  3. 对于意外的 JSON 令牌会引发异常,例如当在需要原语的地方遇到数组时。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何对自定义 JsonConverter 进行单元测试 的相关文章

随机推荐