簡體   English   中英

Mock JsonReader單元如何測試自定義JsonConverter

[英]How Mock JsonReader unit testing a custom JsonConverter

寫了一個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時出錯。路徑''。”

使用DeserializeObject<T>將調用您的ReadJson覆蓋。

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

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

    Assert.IsNotNull(resultCheck);
}

在不知道自定義轉換器的內部工作原理的情況下,您是否可以創建一個測試雙重而不是模擬?

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

來自文檔

雖然直接使用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本身,而是您的自定義轉換器實際執行的操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM