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



编写一个自定义JsonConverter来处理同一api的不同版本返回的不同Json格式。一个应用程序向其他几个应用程序发出请求,我们不知道会返回哪种格式,所以JsonConverter可以处理这一问题,而且似乎工作得很好。我需要在项目中添加单元测试,只是我没有找到有用的资源来帮助Mock完成一些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的值,在本例中为null值,但在其他单元测试中,我想要一个实际的json(JObject)。运行单元测试时,我收到一个"Newtonsoft.JsonReaderException:从JsonReader.Path"读取JObject时出错。">

DeserializeObject<T>的使用将在后台调用ReadJson的覆盖。

[TestMethod]
public void ReadJsonVerifyTypeReturned()
{
var testJson = CreateJsonString();
var result = JsonConvert.DeserializeObject<ProbeResponseData>(testJson);
var resultCheck = result as ProbeResponseData;
Assert.IsNotNull(resultCheck);
}

虽然直接使用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支持。我想将特定的JSONnumber值反序列化为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本身,但您的自定义转换器实际做什么。

在不了解自定义转换器的内部工作原理的情况下,您能创建一个测试替身而不是mock吗?

public class JsonReaderThatReturnsNull : JsonReader
{
public override bool Read()
{
return true;
}
public override object Value => null;
}

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

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

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

来自文档

相关内容

  • 没有找到相关文章

最新更新