【问题标题】:Using xUnit inlineData when a parameter is a long string当参数是长字符串时使用 xUnit inlineData
【发布时间】:2020-02-06 22:01:32
【问题描述】:

我正在尝试将 4 个几乎相同的测试用例转换为一个,其中一个使用 xUnits InlineData。

问题在于,我想要区别的参数是一个长字符串——实际上是一个带有微小变化的 JSON-“字符串”。它是这样的(我只取了相关的):

....
....
        var mockHttp = new Mock<HttpMessageHandler>(MockBehavior.Strict);

        mockHttp
            .Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent( // the below part is the only thing that differs
                    "{'content':[" +
                        "{'Id':'00001', 'eNumber':'001', 'isOwner': true, 'isAlone':false}," +
                        "{'Id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false}," +
                        "{'Id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false}," +
                        "{'Id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false}," +
                        "]}"
                    )
            })
            .Verifiable();

        var httpClient = new HttpClient(mockHttp.Object)
        {
            BaseAddress = new Uri("http://something.com/")
        };

下一个测试包含相同的内容,但带有另一个“内容”,如下所示:

....
....
                   "{'content':[" +
                    "{'Id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false}," +
                    "{'Id':'00001', 'eNumber':'002', 'isOwner': false, 'isAlone':true}," +
                    "{'Id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false}," +
                    "{'Id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false}," +
                    "]}"
....
....

第三个是这样的:

....
....
                   "{'content':[" +
                    "{'Id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false}," +
                    "{'Id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':true}," +
                    "{'Id':'00001', 'eNumber':'007', 'isOwner': true, 'isAlone':true}," +
                    "{'Id':'00001', 'eNumber':'003', 'isOwner': false, 'isAlone':false}," +
                    "]}"
....
....

以此类推,接下来是两个。 所以我想知道是否有一种聪明的方法可以将 5 种类型的内容作为参数放入 InlineData 中 - 从而以某种方式节省大量复制的代码?可能有,但我仍然没有弄清楚怎么做,反正没有写很多代码。

将测试编写为 5 个单独的测试全部变为绿色,因此我唯一要寻找的是一种更智能的代码编写方式,只需更改 StringContent 中的参数/字符串即可使其更简洁、更易于阅读。

根据 cmets 中的问题更新:

我正在测试的课程是这样的:

....
....

        var httpResponseMessage = await User(// something);


        if (!httpResponseMessage .IsSuccessStatusCode) return;

        var content = await httpResponseMessage.Content.ReadAsStringAsync();
        var deserializedContent = JsonConvert.DeserializeObject<UserModel>(content); // it fails here...

....
....

我的 UserModel 类如下所示:

public class UserModel
{
    public UserModelDto[] {get; set;}
}

我的 UserModelDto 类看起来像这样:

    public class UserModelDto
{
    public string Id{ get; set; }
    public string ENumber { get; set; }
    public bool IsOwner { get; set; }
    public bool IsAlone { get; set; }
}

【问题讨论】:

  • 您可以创建一个模型来保存数据并使用MemberDataAttribute 来使用IEnumerable&lt;object[]&gt; 作为您的测试源,其中该测试源的元素是您的对象模型的数组。在测试中,您可以将这些对象序列化为字符串。

标签: c# json testing serialization xunit


【解决方案1】:

扩展我的评论并提供答案,您可以使用MemberDataAttributeClassDataAttribute 来实现此目的。此示例使用MemberDataAttribute

首先创建一个类来表示您的模型。

public class UserModel
{
    [JsonProperty("content")]
    public UserModelDto[] {get; set;}
}

public class UserModelDto
{
    [JsonProperty("id")]
    public string Id{ get; set; }
    [JsonProperty("eNumber")]
    public string ENumber { get; set; }
    [JsonProperty("isOwner")]
    public bool IsOwner { get; set; }
    [JsonProperty("isAlone")]
    public bool IsAlone { get; set; }
}

public static IEnumerable&lt;object[]&gt; 属性或方法添加到您的测试类,其中还包含您的测试场景。

public static IEnumerable<object[]> TestCaseScenarios
    => new object[][]
       {
           new object[]
           {
               new UserModel
               {
                   Contents = new[]
                   {
                       new UserModelDto
                       {
                           Id = "00001",
                           ENumber = "no",
                           IsOwner = false,
                           IsAlone = false
                       },
                       new UserModelDto
                       {
                           Id = "00001",
                           ENumber = "007",
                           IsOwner = true,
                           IsAlone = true
                       },
                   }
               }
           },
           new object[] { } //etc
       }

MemberDataAttribute 应用于您的测试

[Theory] // Tests which accept parameters require the TheoryAttribute, instead of FactAttribute
[MemberData(nameof(TestCaseScenarios))]
public async Task PerformingAction_WithCriteria_ReturnsExpectedThing(UserModel userModel)
{
    // Serialize userModelto create a string
    var userModelAsString = JsonConvert.SerializeObject(userModel);
    // Create the StringContent object
    var stringContent = new StringContent(userModelAsString);
    // Create a HttpClient whose BaseAddress is the host address

    var mockHttp = new Mock<HttpMessageHandler>(MockBehavior.Strict);

    mockHttp
        .Protected()
        .Setup<Task<HttpResponseMessage>>(
            "SendAsync",
            ItExpr.IsAny<HttpRequestMessage>(),
            ItExpr.IsAny<CancellationToken>())
        .ReturnsAsync(new HttpResponseMessage()
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(stringContent)
        })
        .Verifiable();

    var httpClient = new HttpClient(mockHttp.Object) { BaseAddress = new Uri("http://something.com") };
}

【讨论】:

  • 是的,好吧 - 我希望能够将 JSON(或字符串)作为字符串放置,而不是在每个测试中将它们拆分为 Id、eNumber、IsOwner、IsAlone,但是这样做时使用“... new StringContent(JsonConvert.SerializeObject(testData))”进行转换失败
  • “当这样做时它在转换中失败”是什么意思?错误信息是什么?
  • "错误转换值 "{'content':[{'id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':true},{ 'id':'00001', 'eNumber':'001', 'isOwner': false, 'isAlone':false},{'id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false},{'id':'00001', 'eNumber':'no', 'isOwner': false, 'isAlone':false},]}" 输入 'userModelDto'"。我已经用有关课程的更多信息更新了我的问题 - 希望我说得通。
  • @badaboomskey 更新的代码无效,您在UserModel 中缺少属性名称,我已更新我的答案以更改模型类的设计以匹配您的。跨度>
  • Arrghh - 我发现我做错了什么。反序列化和序列化部分导致失败。从您的回答中获得了很大的启发,但使用包含整个“内容”的单个字符串,我保存了许多代码行。太棒了,感谢您的快速回答。睡了一会儿后,我会用解决方案更新我的问题 - 被这个问题困扰了好几个小时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
  • 2017-10-23
  • 1970-01-01
相关资源
最近更新 更多