- Home
- .NET tutorials
- How to use InlineData, MemberData and ClassData in xUnit
How to use InlineData, MemberData and ClassData in xUnit
Published: Monday 22 September 2025
If you aren't using the Theory
attributes InlineData
, MemberData
, or ClassData
, you aren't taking full advantage of xUnit's capabilities.
Understanding when to use these attributes will make your tests more robust and maintainable.
What we are testing
We are going to test two methods. The first converts a speed from mph to kph. The other does the opposite - converts kph to mph.
// SpeedConversionHelper.cs
public static class SpeedConversionHelper
{
public static decimal ConvertToKph(decimal mph)
{
return mph * 1.6093m;
}
public static decimal ConvertToMph(decimal kph)
{
return kph / 1.6093m;
}
}
If you are only using Fact
Fact
is used for straightforward, single-scenario tests in xUnit. It has no parameters and does not depend on external data. Use the [Fact]
attribute about your test method to declare your unit test with this scenario:
// SpeedConversionFactTests.cs
public class SpeedConversionFactTests
{
[Fact]
public void ConvertToKph_Fact_ReturnsCorrectSpeed()
{
var act = SpeedConversionCalculator.ConvertToKph(100);
Assert.StrictEqual(160.93m, act);
}
[Fact]
public void ConvertToMph_Fact_ReturnsCorrectSpeed()
{
var act = SpeedConversionCalculator.ConvertToMph(160.93m);
Assert.StrictEqual(100, act);
}
}
However, if you want to add multiple inputs for your unit tests, you will need something else.
Use Theory
for parameterised tests
Theory
is similar to Fact
, but it can take parameters. This allows you to run the same test logic with multiple data sets making it perfect for data-driven testing.
There are a number of different attributes that you can use with Theory
as to where you can get the data from.
Use InlineData
for simple datasets
The InlineData
attribute allows you to pass arguments directly into a test.
// SpeedConversionInlineDataTests.cs
public class SpeedConversionInlineDataTests
{
[Theory]
[InlineData(100, 160.93)]
[InlineData(50, 80.465)]
public void ConvertToKph_Theory_ReturnsCorrectSpeed(decimal mph, decimal expectedKph)
{
var act = SpeedConversionCalculator.ConvertToKph(mph);
Assert.StrictEqual(expectedKph, act);
}
[Theory]
[InlineData(321.86, 200)]
[InlineData(241.395, 150)]
public void ConvertToMph_Theory_ReturnsCorrectSpeed(decimal kph, decimal expectedMph)
{
var act = SpeedConversionCalculator.ConvertToMph(kph);
Assert.StrictEqual(expectedMph, act);
}
}
The way it works is that you pass parameters into the InlineData
attribute and these match the parameters in the same order as the test method.
This is best for small, simple datasets. You can use the InlineData
attribute multiple times for the same test, but it can clutter the tests if it's used too often.
One thing with the InlineData
attribute is that the values must be hardcoded. You can't perform any sort of calculation. In addition, the values can't be used with other tests making it inflexible.
Use MemberData
for pulling data from a property or method
The MemberData
attribute lets you supply test data from a static property, method, or field. This should be used for larger data sets or when the data needs calculation before being passed to the test.
In this example, we've created two static methods returning a list of an object array. The object array gets translated to each of the parameters in the test method in the same order as the array index.
In the MemberData
attribute, we've specified the static method by calling the nameof
expression followed by the name of the static method:
// SpeedConversionHelperMemberDataObjectTests.cs
public class SpeedConversionHelperMemberDataObjectTests
{
public static IList<object[]> MphToKphTestData =>
new List<object[]>
{
new object[] { 100, 100 * 1.6093 },
new object[] { 150, 150 * 1.6093 }
};
public static IList<object[]> KphToMphTestData =>
new List<object[]>
{
new object[] { 200, 200 / 1.6093m },
new object[] { 150, 150 / 1.6093m }
};
[Theory]
[MemberData(nameof(MphToKphTestData))]
public void ConvertToKph_MemberData_ReturnsCorrectSpeed(decimal mph, decimal expectedKph)
{
var act = SpeedConversionCalculator.ConvertToKph(mph);
Assert.StrictEqual(expectedKph, act);
}
[Theory]
[MemberData(nameof(KphToMphTestData))]
public void ConvertToMph_MemberData_ReturnsCorrectSpeed(decimal kph, decimal expectedMph)
{
var act = SpeedConversionCalculator.ConvertToMph(kph);
Assert.StrictEqual(expectedMph, act);
}
}
Use ClassData
for supplying data from a separate class
The ClassData
attribute is very similar to MemberData
, but the test data parameters are supplied from a class.
We've created classes for our test data which implements IEnumerable<object[]>
. The data is stored in the _data
field and then enumerated as part of the public GetEnumerator
method.
We then use the ClassData
attribute and specify the class by using the typeof
expression:
// SpeedConversionHelperClassDataObjectTests.cs
public class SpeedConversionHelperClassDataObjectTests
{
public class SpeedMphToKphDataClass : IEnumerable<object[]>
{
private readonly IList<object[]> _data = new List<object[]>
{
new object[] {100, 100 * 1.6093m},
new object[] {200, 200 * 1.6093m}
};
public IEnumerator<object[]> GetEnumerator() => _data.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public class SpeedKphToMphDataClass : IEnumerable<object[]>
{
private readonly IList<object[]> _data = new List<object[]>
{
new object[] {300, 300 / 1.6093m},
new object[] {200, 200 / 1.6093m }
};
public IEnumerator<object[]> GetEnumerator() => _data.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
[Theory]
[ClassData(typeof(SpeedMphToKphDataClass))]
public void ConvertToKph_ClassData_ReturnsCorrectSpeed(decimal mph, decimal expectedKph)
{
var act = SpeedConversionCalculator.ConvertToKph(mph);
Assert.StrictEqual(expectedKph, act);
}
[Theory]
[ClassData(typeof(SpeedKphToMphDataClass))]
public void ConvertToMph_ClassData_ReturnsCorrectSpeed(decimal kph, decimal expectedMph)
{
var act = SpeedConversionCalculator.ConvertToMph(kph);
Assert.StrictEqual(expectedMph, act);
}
}
Use TheoryData
for strongly typed parameters
With MemberData
and ClassData
, if you want to your parameters to be strongly typed, you can use the TheoryData
type instead of object[]
. This allows you to pass generic types dependant on what parameter types you are using in your unit tests.
When using MemberData
, the static method returns the TheoryData
class with it's generic types. As we are using two decimals in our data sets, we would add two decimal
generic types.
// SpeedConversionHelperMemberDataTheoryDataTests.cs
public class SpeedConversionHelperMemberDataTheoryDataTests
{
public static TheoryData<decimal, decimal> MphToKphTestData =>
new TheoryData<decimal, decimal>
{
{ 100, 100 * 1.6093m },
{ 150, 150 * 1.6093m }
};
public static TheoryData<decimal, decimal> KphToMphTestData =>
new TheoryData<decimal, decimal>
{
{ 200, 200 / 1.6093m },
{ 150, 150 / 1.6093m }
};
[Theory]
[MemberData(nameof(MphToKphTestData))]
public void ConvertToKph_MemberData_ReturnsCorrectSpeed(decimal mph, decimal expectedKph)
{
var act = SpeedConversionCalculator.ConvertToKph(mph);
Assert.StrictEqual(expectedKph, act);
}
[Theory]
[MemberData(nameof(KphToMphTestData))]
public void ConvertToMph_MemberData_ReturnsCorrectSpeed(decimal kph, decimal expectedMph)
{
var act = SpeedConversionCalculator.ConvertToMph(kph);
Assert.StrictEqual(expectedMph, act);
}
}
If you are using ClassData
, the class will inherit the TheoryData
class with it's generic types. When you create an instance of the class, you can call the Add
method in the constructor to add the data values that you wish to add in your unit tests.
// SpeedConversionHelperClassDataTheoryDataTests.cs
public class SpeedConversionHelperClassDataTheoryDataTests
{
public class SpeedMphToKphDataClass : TheoryData<decimal, decimal>
{
public SpeedMphToKphDataClass()
{
Add(100, 100 * 1.6093m);
Add(200, 200 * 1.6093m);
}
}
public class SpeedKphToMphDataClass : TheoryData<decimal, decimal>
{
public SpeedKphToMphDataClass()
{
Add(300, 300 / 1.6093m);
Add(200, 200 / 1.6093m);
}
}
[Theory]
[ClassData(typeof(SpeedMphToKphDataClass))]
public void ConvertToKph_ClassData_ReturnsCorrectSpeed(decimal mph, decimal expectedKph)
{
var act = SpeedConversionCalculator.ConvertToKph(mph);
Assert.StrictEqual(expectedKph, act);
}
[Theory]
[ClassData(typeof(SpeedKphToMphDataClass))]
public void ConvertToMph_ClassData_ReturnsCorrectSpeed(decimal kph, decimal expectedMph)
{
var act = SpeedConversionCalculator.ConvertToMph(kph);
Assert.StrictEqual(expectedMph, act);
}
}
Watch the video
You'll learn about the power of using InlineData
, MemberData
, or ClassData
when you watch this video to master these Theory
attributes.
In addition, when you download the code example, you'll be able to try each of these methods out for yourself.
When to use these types
In summary, use Fact
for small static tests and Theory
for parameterised, data-driven tests. With Theory
, use InlineData
for small sets of hard coded data and either MemberData
, or ClassData
if you require your data to be calculated.
Using this additional functionality will not only get you to take full advantage of xUnit's capabilities, but it will keep your tests robust and readable.
Latest tutorials

