Creating parameterised tests in xUnit with [InlineData], [ClassData], and [MemberData]

In this post I provide an introduction to creating parmeterised tests using xUnit’s [Theory] tests, and how you can pass data into your test methods. I’ll cover the common [InlineData] attribute, and also the [ClassData] and [MemberData] attributes. In the next post, I’ll show how to load data in other ways by creating your own [DataAttribute].

If you’re new to testing with xUnit, I suggest reading the getting started documentation. This shows how to get started testing .NET Core projects with xUnit, and provides an introduction to [Fact] and [Theory] tests.

xUnit uses the [Fact] attribute to denote a parameterless unit test, which tests invariants in your code.

In contrast, the [Theory] attribute denotes a parameterised test that is true for a subset of data. That data can be supplied in a number of ways, but the most common is with an [InlineData] attribute.

The following example shows how you could rewrite the previous CanAdd test method to use the [Theory] attribute, and add some extra values to test:

Using the [Theory] attribute to create parameterised tests with [InlineData]

[Theory]
[InlineData(1, 2, 3)]
[InlineData(-4, -6, -10)]
[InlineData(-2, 2, 0)]
[InlineData(int.MinValue, -1, int.MaxValue)]
public void CanAddTheory(int value1, int value2, int expected)
{
    var calculator = new Calculator();

    var result = calculator.Add(value1, value2);

    Assert.Equal(expected, result);
}

Instead of specifying the values to add (value1 and value2) in the test body, we pass those values as parameters to the test. We also pass in the expected result of the calculation, to use in the Assert.Equal() call.

The data is provided by the [InlineData] attribute. Each instance of [InlineData] will create a separate execution of the CanAddTheory method. The values passed in the constructor of [InlineData] are used as the parameters for the method – the order of the parameters in the attribute matches the order in which they’re supplied to the method.

The [InlineData] attribute is great when your method parameters are constants, and you don’t have too many cases to test. If that’s not the case, then you might want to look at one of the other ways to provide data to your [Theory] methods.

Using a dedicated data class with [ClassData]

If the values you need to pass to your [Theory] test aren’t constants, then you can use an alternative attribute, [ClassData], to provide the parameters. This attribute takes a Type which xUnit will use to obtain the data:

[Theory]
[ClassData(typeof(CalculatorTestData))]
public void CanAddTheoryClassData(int value1, int value2, int expected)
{
    var calculator = new Calculator();

    var result = calculator.Add(value1, value2);

    Assert.Equal(expected, result);
}

For Complex data Type Example:

 public class Car
    {
        public int Id { get; set; }
        public    long Price { get; set; }
        public Manufacturer Manufacturer { get; set; }
    }
    public class Manufacturer
    {
        public string Name { get; set; }
        public string Country { get; set; }
    }


public class CarClassData : IEnumerable<object[]>
    {
        public Car car;
        public void PrepareData()
        {
            car = new Car
            {
                Id = 1,
                Price = 36000000,
                Manufacturer = new Manufacturer
                {
                    Country = "country",
                    Name = "name"
                }
            };
        }

        public IEnumerator<object[]> GetEnumerator()
        {
            PrepareData();

            yield return new object[] { car };
        }
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }


         [Theory]
        [ClassData(typeof(CarClassData))]
        public void CarTest(Car car)
        {
            var output = car;
        }

Creating Test Data for LinList:

public class ListNodeTestData : IEnumerable
{
public ListNode l1, l2, expectedResult;
public object[] PrepareData()
{
var l1 = new ListNode(2);
ListNode.InsertEnd(l1, 4);
ListNode.InsertEnd(l1, 3);

        var l2 = new ListNode(8);
        ListNode.InsertEnd(l2, 6);
        ListNode.InsertEnd(l2, 8);

        var expectedResult = new ListNode(1);
        ListNode.InsertEnd(expectedResult, 1);
        ListNode.InsertEnd(expectedResult, 1);
        ListNode.InsertEnd(expectedResult, 1);

        return new object[] { l1, l2, expectedResult };
    }

    public IEnumerator<object[]> GetEnumerator()
    {
        yield return PrepareData();
    }


    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
    [Theory]
    [ClassData(typeof(ListNodeTestData))]   
     public void MyTheory(ListNode a,ListNode b, ListNode expectedResult)
        {
            var result = _addTwoNumber.AddTwoNumbers(a, b);
            Assert.NotNull(result);
            Assert.Equal(expectedResult, result, new LinkedListCompare());
        }

Published by codeblogforfun

Coder, blogger, traveler

Leave a comment

Design a site like this with WordPress.com
Get started