5

I was doing unit test using xUnit package for my class library. I would like to try on JSON approach rather than normal object approach for data-driven unit test. However I don't see anywhere that able to achieve this.

What I am stucking at is data-driven test MemberData require the test data to be present in IEnumerable<object[]> type, when I call data from JSON file, it is in my data model class typed. Do I add them into the IEnumerable<object[]>? Or there is another way to do it?

Here's the code I have so far:

This is the json file which holds 5 test data:

{
  "user": [
    {
      "name": "John",
      "age": 12,
      "gender": "Male",
      "Hobby": "Reading"
    },
    {
      "name": "Susan",
      "age": 34,
      "gender": "Female",
      "Hobby": "Gardening"
    },
    {
      "name": "Larry",
      "age": 24,
      "gender": "Male",
      "Hobby": "Gaming"
    },
    {
      "name": "Jack",
      "age": 3,
      "gender": "Male",
      "Hobby": "Sleeping"
    },
    {
      "name": "Minnie",
      "age": 15,
      "gender": "Female",
      "Hobby": "Partying"
    }
  ]
}

This is Test class file:

private User GetTestData()
{
     var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestData.json"); 
     var reader = new StreamReader(filePath);
     var jsonStr = reader.ReadToEnd();
     var jsonObj = JObject.Parse(jsonStr);

     var testDataObj = jsonObj["user"].ToString();
     var testData = JsonConvert.DeserializeObject<User>(testDataObj);

     return testData;
}

public static IEnumerable<object[]> ValidUserTestData()
{
     //What should I do here?   
}

[Theory]
[MemberData(nameof(ValidUserTestData))]
public void Test1()
{

}

Is this the correct way to implement this type of test? Or is there any other better options to use JSON file inside data-drive test?

Thank you.

1 Answer 1

11

MemberData attribute accepts an IEnumerable<object[]> of objects, which will be passed to your test method one by one. So, you should deserialize your JSON into array of objects (your json sample contains an array of User objects), and then return it from ValidUserTestData method. Also make sure, that Test1() accepts a correct parameter

[Theory]
[MemberData(nameof(ValidUserTestData))]
public void Test1(User user)
{
    //test logic against every User instance
}

ValidUserTestData can be declared per following: read the json first, than convert it to the enumeration of User objects and return it as an IEnumerable<object[]>

public static IEnumerable<object[]> ValidUserTestData()
{
    var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestData.json");
    var json = File.ReadAllText(filePath);
    var jobject = JObject.Parse(json);
    var users = jobject["user"]?.ToObject<IEnumerable<User>>();

    foreach (var user in users ?? Enumerable.Empty<User>())
    {
        yield return new[] { user };
    }
}

Every single item in IEnumerable<object[]> represents an set of parameters, accepted by the test method. You'll need only one input parameter in your test method, so every object[] contains single user instance.

There is very nice article about it Creating parameterized tests in xUnit

Sign up to request clarification or add additional context in comments.

2 Comments

Note 1: Had to use the "Copy to Output Directory" file property settings set to "Copy Always" for this to work.
Note 2: if you're able to use System.Text.Json, you can simply do return JsonSerializer.Deserialize<IEnumerable<User>>(json); ... i.e. no need for the JObject, the ToObject method call, nor the foreach. Yes, you'll get a JsonReaderException if the file is empty, but this is a static xUnit file, it probably doesn't make sense for it to be empty anyway.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.