xUnit Theory: Working With InlineData, MemberData, ClassData

xUnit support two different types of unit test, Fact and Theory. We use xUnit Fact when we have some criteria that always must be met, regardless of data. For example, when we test a controller’s action to see if it’s returning the correct view. xUnit Theory on the other hand depends on set of parameters and its data, our test will pass for some set of data and not the others. We have a theory which postulate that with this set of data, this will happen. In this post, I’m going to discuss what are our options when we need to feed a theory with a set of data and see why and when to use them.

xUnit Theory With InlineData

This is a simplest form of testing our theory with data, but it has its drawbacks, which is we don’t have much flexibility, let’s see how it works first.

As you see above, we provide some values in InlineData and xUnit will create two tests and every time populates the test case arguments with what we’ve passed into InlineData. I said there are some limitation on what we can pass in InlineData attribute, look what happens when we try to pass a new instance of some object:

InlineData Attribute Doesn't Work With Complex Types

We can pass this kind of data to our theory with ClassData or MemberData.

xUnit Theory With ClassData

ClassData is another attribute that we can use with our theory, with ClassData we have more flexibility and less clutter:

Here I’ve created a class that inherits from IEnumerable<object[]>, note that it has to be an object, otherwise xUnit will throws an error. Next I create a private list of object that I intend to pass to my theory and finally I implemented the GetEnumerator method with piggybacking on our list Enumerator. Now we can pass our TestDataGenerator class to ClassData attribute and the returned data form that class will populate the test case’s parameters.

xUnit Theory With MemberData

MemberData gives us the same flexibility but without the need for a class. I’ve created an static method called GetNumbers which is local to our test class, and I passed it to AllNumbers_AreOdd_WithMemberData‘s MemberData attribute. But it doesn’t need to be a local method, we can pass a method from another class too, as I did with AllNumbers_AreOdd_WithMemberData_FromDataGenerator test case. Also you’re not limited to primitive types, I’ve generated and passed a complex object called Person to AllPersons_AreAbove14_WithMemberData_FromDataGenerator test, and this was something that we couldn’t do with InlineData attribute, but we can do with ClassData or MemberData attribute.

Share...
 

Hamid Mosalla

Hi, I'm Hamid Mosalla, I'm a software developer, indie cinema fan and a classical music aficionado. Here I write about my experiences mostly related to web development and .Net.

 

11 thoughts on “xUnit Theory: Working With InlineData, MemberData, ClassData

  1. hey, I have a requirement to invoke a test method (with memberdata attribute) using reflection. Can you please help me achieve this?

  2. Very nice. Thank you for taking the time to put this together. I’ve struggled a bit with the documentation they have on github.io. This makes it so much easier. Thanks again!

    1. Attributes for InlineData need constant expressions, e.g int, bool, string etc. So you can’t use DateTime with InlineData, but you can use it with ClassData. Let me know if you had any problem with that, or ask a question on stackoverflow and I’ll answer.

    2. @jul_po:disqus You can use DateTime types in InlineData by declaring them as strings in your attributes and parsing the string to a DateTime within your test, e.g.:

      [Theory]
      [InlineData(“1988-05-01”, false)]
      [InlineData(“1991-02-07”, true)]
      public void PerformTest(String date, Boolean expectedResult)
      {
      Assert.Equal(expectedResult, IsInNineties(DateTime.Parse(date)));
      }

  3. In C# 11, you can use

    public class EnumDataAttribute : DataAttribute where T : struct, System.Enum
    {
    private readonly IEnumerable _value = Enum.GetValues().Select(e => new object[] { e });
    public override IEnumerable GetData(MethodInfo testMethod) => _value;
    }

Leave a Reply

Your email address will not be published. Required fields are marked *