nelion
nelion

Reputation: 1962

Using xUnit inlineData when a parameter is a long string

I am trying to convert 4 almost identical testcases into one, where the one instead uses xUnits InlineData.

The thing is that the parameter, which I want to differ is a long string - actually a JSON-"string" with small changes. It goes like this (I've only taken the relevant):

....
....
        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/")
        };

The next test contains the same, but with another 'content', that goes like this:

....
....
                   "{'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}," +
                    "]}"
....
....

and a third goes like this:

....
....
                   "{'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}," +
                    "]}"
....
....

and so forth with the two next. So I am wondering if there is a smart way to put the 5 types of contents inside a InlineData as a parameter - and thereby somehow save a lot of copied code? There probably is, but I still havent figured out how, without actually writing a lot of code anyway.

Writing the tests as 5 individual tests all goes green, so the only thing I am looking for is a smarter way to write the code, making it cleaner and easier to read, by only changing the parameter/string inside StringContent.

Update as per question in comments:

The class I am testing goes something like this:

....
....

        var httpResponseMessage = await User(// something);


        if (!httpResponseMessage .IsSuccessStatusCode) return;

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

....
....

my UserModel class looks like this:

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

and my UserModelDto class looks like this:

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

Upvotes: 0

Views: 1266

Answers (2)

AngelaG
AngelaG

Reputation: 735

FYI for those who come across this question in google when searching for Xunit inlinedata for a long variable.

this was giving errors. [InlineData(0)]

this is the solution [InlineData((long)0)] where 0 is my input variable.

Upvotes: 0

ColinM
ColinM

Reputation: 2681

Extending on my comment and providing an answer, you can use MemberDataAttribute or ClassDataAttribute to achieve this. This example uses MemberDataAttribute.

First create a class to represent your model.

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

Add a public static IEnumerable<object[]> property, or method, to your test class, which also contains your test scenarios.

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
       }

Apply the MemberDataAttribute to your test

[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") };
}

Upvotes: 2

Related Questions