Search code examples
xunitdatarow

Xunit Method on Datarow


I would be grateful for assistance with the following question. I would like to evaluate one or more data series with Xunit. For this I have programmed a simple example. Read the data series 10,20,30,40,80 and add 1 with a working method testAddValue.

  public class TestDataRow
    {
        [Theory]
        [MemberData(nameof(DataRowValue))]
        [MemberData(nameof(ExpectedDataRowValue))]

        public void TestDataRow_Method(List<int> TestValue, List<int> ExpectedValue)  

        {
            // Init Method
            Method testAddValue = new Method();

            // Loop 
            for (int i = 0; i < TestValue.Count; i++)
            {
                var actual = testAddValue.TestDataRow_AddValue(TestValue[i], 1);
                Assert.Equal(TestValue[i], ExpectedValue[i]);
            }
        }

        public static IEnumerable<object[]> DataRowValue()
        {
            var testRow = new List<List<int>>
                {
                    // TestValue
                    new List<int>{ 10, 20, 30, 40, 80},
                };
            yield return new object[] { testRow };
        }

        public static IEnumerable<object[]> ExpectedDataRowValue()
        {
            var expectedtestRow = new List<List<int>>
                {
                    // ExpectedValue
                    new List<int>{ 11, 21, 31, 41, 81},
                };
            yield return new object[] { expectedtestRow };
        }
    }

The compiler gives no error message.

When I run the test with TestDataRow_Method(List TestValue), I get the message: Object of type 'System.Collections.Generic.List1[System.Collections.Generic.List1[System.Int32]]' cannot be converted to type 'System.Collections.Generic.List`1[System.Int32]'. I don't understand this error message....

When I run the test with TestDataRow_Method(List TestValue, List ExpectedValue), I get the message that ExpectedValue is not present. This is surely a consequential error that should be taken care of by solving the above problem. For a better understanding of my approach, I am posting the full code.

What am I doing wrong?


Solution

  • First, a comment: you've assigned a value to actual but you never use it. Instead, you assert that TestValue[i] is equal to ExpectedValue[i] which I hardly believe is your real intention here.

    Your test method expects two arguments, so the MemberData methods have to yield an array with two elements. The first array element would be the list of test values, the second array element would be the list of expected values. As programmed, each MemberData methods returns an array with one element (a single list), so there is nothing for the second parameter of your test method.

    The MemberData are not executed in parallel, but rather sequentially one after the other, first each output from DataRowValue is given to the test method and it is executed, then each output from ExpectedDataRowValue. As programmed, they each only return a single list, so you effectively have two specific separate test cases here.

    I think your intention might be more along these lines (since you didn't include your class Method, I made a guess about what the method might be doing):

    using System;
    using System.Collections.Generic;
    using Xunit;
    
    namespace SO74067556
    {
        public class Method
        {
            public int TestDataRow_AddValue(int a, int b)
            {
                return a + b;
            }
        }
    
        public class TestDataRow
        {
            [Theory]
            [MemberData(nameof(TestData))]
    
            public void TestDataRow_Method(int TestValue, int ExpectedValue)
            {
                // Arrange
                Method testAddValue = new Method();
    
                // Act
                var actual = testAddValue.TestDataRow_AddValue(TestValue, 1);
    
                // Assert
                Assert.Equal(actual, ExpectedValue);
            }
    
            public static IEnumerable<object[]> TestData()
            {
                var data = new List<Tuple<int, int>>()
                    {
                        // Test values 
                        new Tuple<int, int>(10,11),
                        new Tuple<int, int>(20,21),
                        new Tuple<int, int>(30,31),
                        new Tuple<int, int>(40,41),
                        new Tuple<int, int>(80,81)
                    };
                foreach (Tuple<int, int> item in data)
                {
                    yield return new object[] { item.Item1, item.Item2 };
                }
            }
        }
    }
    

    For simple integer data like this, however, you would be better served by using InlineData:

    [Theory]
            //[MemberData(nameof(TestData))]
            [InlineData(10, 11)]
            [InlineData(20, 21)]
            [InlineData(30, 31)]
            [InlineData(40, 41)]
            [InlineData(80, 81)]
    
            public void TestDataRow_Method(int TestValue, int ExpectedValue)
            {
                // Arrange
                Method testAddValue = new Method();
    
                // Act
                var actual = testAddValue.TestDataRow_AddValue(TestValue, 1);
    
                // Assert
                Assert.Equal(actual, ExpectedValue);
            }
    

    This is not only has less code, it is also easier to understand and the individual test cases show up in the Test Explorer in Visual Studio.