If you are anything like me, you avoided data-driven tests in MSTest because they were such a pain to write and maintain. However, I know not everyone is like me and I also know that even though we try to avoid things, we do not always succeed. So, in this entry in the series on migrating from MSTest to XUnit, we will look at migrating your data-driven tests, but before we get into the details, let's briefly recap on what MSTest provides for data-driving tests; the DataSource
attribute.
The DataSource
attribute specifies a data source from which data points are loaded. The test can then reference the specific data row in the data source from the TestContext
's DataRow
property. You may recall that we touched on the jack-of-all-trades, master-of-none TestContext
back in the entry on outputting from our tests. As MSDN explains, given a table of data rows like this:
FirstNumber | SecondNumber | Sum |
---|---|---|
0 | 1 | 1 |
1 | 1 | 2 |
2 | -3 | -1 |
DataSource
is used like this:
[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0; Data Source=C:\Data\MathsData.sdf;", "Numbers")] [TestMethod()] public void AddIntegers_FromDataSourceTest() { var target = new Maths(); // Access the data int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]); int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]); int expected = Convert.ToInt32(TestContext.DataRow["Sum"]); int actual = target.IntegerMethod(x, y); Assert.AreEqual(expected, actual, "x:<{0}> y:<{1}>", new object[] {x, y}); }
I do not recall using this attribute in earnest, and though perhaps others think of it more fondly, I found it a frustration to use. Thankfully, XUnit is much more inline with my intuition1.
Data-driven test methods in XUnit are called theories and are adorned with the Theory
attribute2. The Theory
attribute is always accompanied by at least one data attribute which tells the test runner where to find data for the theory. There are three built-in attributes for providing data: InlineData
, MemberData
, and ClassData
. Third-party options are also available (check out AutoFixture and its AutoData
attribute) and you can create your own if you find it necessary.
InlineData
provides a simple way to describe a single test point for your theory; MemberData
takes the name of a static member (method, field, or property) and any arguments it might need to generate your data; and ClassData
takes a type that can be instantiated to provide the data. A single test point is provided as an array of type object
, and while XUnit provides the TheoryData
types to allow strongly-typed declaration of test data points, fundamentally, every data source is IEnumerable
. Finally, rather than the obscure TestContext.DataRow
property, data points are provided to a theory test method via the test method's arguments.
So, given all this information, the above example from MSDN could be expressed as follows:
[InlineData(0, 1, 1)] [InlineData(1, 1, 2)] [InlineData(2, -3, -1)] [Theory] public void AddIntegers_FromDataTest(int x, int y, int expected) { //Arrange var target = new Maths(); // Act int actual = target.IntegerMethod(x, y); // Assert Assert.AreEqual(expected, actual, "x:<{0}> y:<{1}>", new object[] {x, y}); }
I took the liberty of using the arrange/act/assert test layout as part of this rewrite as I think it enhances test readability. However, you can see from this example how much easier data-driven testing is under XUnit when compared with traditional MSTest3.
Of course, I totally skipped the fact that the MSTest example used a database for the test data source. That was deliberate to simplify the example, but if we still wanted to use a database to obtain our data points (or some other data source), we can leverage the MemberData
or ClassData
attributes, or even roll our own.
That brings us to the end of this post on migrating data-driven tests from MSTest to XUnit. This also brings us almost to the end of the whole series on migrating from MSTest to XUnit; if you think I missed something important, please leave a comment. Next time, we will finish up with a look at some of the bits around running XUnit tests such as parallel execution, test runners, and the like.
- in case you hadn't noticed, I like XUnit [↩]
- instead of [Fact] as on non-data-driven tests [↩]
- This approach makes so much sense that MSTest introduced it for phone and WinRT app tests and is bringing it to everyone with MSTest version 2 [↩]
How to handle data present in Excel? Previously there is data attribute called 'ExcelData' in xunit. But now it is not defined in xunit reference. is there any other way to utilise data from excel using xunit?
Hi Rakesh,
The xunit documentation includes
ExcelData
as an example, see here.The sample code for
ExcelData
is here on GitHub. It uses theDataAttribute
base class to provide a custom implementation that connects to Excel data using ODBC.I hope this helps.
Cheers,
Jeff