[英]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));
Whilst using JsonConvert
or JsonSerializer
directly will allow you to test it, you probably should make your converter tests a little more direct. 虽然直接使用
JsonConvert
或JsonSerializer
可以让你测试它,但你可能应该让你的转换器测试更直接。 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. 更进一步 ,我可能会模拟元素,例如
JsonReader
和JsonSerializer
从而产生不同的前提条件,因此我可以测试各种各样的场景。
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. 依赖于
JsonConvert
或JsonSerializer
来运行完整反序列化过程的问题在于,您正在引入其他逻辑,这些逻辑在很大程度上是您无法控制的。 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.