A beginners guide to xUnit

Published: Thursday 25 July 2019

Over the last few weeks, I've been exploring the functionality of XUnit. For anyone who doesn't know, XUnit is one of the unit testing frameworks that are available for .NET.

C# coding challenges

C# coding challenges

Our online code editor allows you to compile the answer.

Here are some of the topics I'm going to cover.

How to set up a test project

In-order to create a test, you need to first set up an XUnit project. You should be able to do that in Visual Studio by creating a new project.

Within that project, you can set up a class and create methods within that class.

But how does XUnit know which tests it needs to run? Well you can apply the "Fact" attribute to each method that you wish XUnit to run. XUnit will then know to run this test.

// Test1.cs    
public class Test1
{

	[Fact]
	public void TestPattern()
	{
		var password = "TheBeast";

		Assert.True(IsPasswordValid(password));

		password = "324idfdfdf";
		Assert.False(IsPasswordValid(password));
	}

	public bool IsPasswordValid(string password)
	{
		return password == "TheBeast";
	}
}

As you can see from the above example, I've created two methods. The TestPattern method has the "Fact" attribute assigned to it. Inside that method, there are a number of Assert calls within it. This is where you conduct your tests. XUnit allows you to test on many different things, and here is an example of some of the Assert calls that can be made:

  • Contains - Whether a string contains a certain word
  • Empty - Whether an IEnumerable is empty
  • Equal - Pass in an expected and actual value
  • IsNotNull - Pass in an object to see if it has been initalised
  • True - Pass in a condition to see if it's true

How each test runs

Even if you have multiple test methods in a test class, each test will always initalise a new instance of the test class. This means that if you wish to run some code before your test commences, you can do so in the constructor. 

So what if you want to run some code after a test has progressed? You may wish to log that the test has completed. Well you can inherit the IDisposable interface, and include the Dispose method.

// Test1.cs   
public class Test1 : IDisposable
{
	public Test1()
	{
		// I can run some code before the test runs
	}

	[Fact]
	public void TestPattern()
	{
		var password = "TheBeast";

		Assert.True(IsPasswordValid(password));
		password = "324idfdfdf";
		Assert.False(IsPasswordValid(password));
	}

	[Fact]
	public void TestPattern2()
	{
		Assert.True(true);
	}

	public bool IsPasswordValid(string password)
	{
		return password == "TheBeast";
	}

	public void Dispose()
	{
		// I can run some code after the test has finished
	}
}

Using the same test for multiple values

If you wish to test multiple values in the same test, rather than creating additional methods to accommodate for this, you can use the "Theory" attribute.

The "Theory" attribute is the same as the "Fact" attribute in the sense that XUnit knows the method is a test. But you have to include additional attributes to a method to allow to pass in multiple values.

One way you can do this is with the "InlineData" attribute. The "InlineData" attribute allows you to pass in an object array with each index representing a parameter in the method. And you can include multiple "InlineData" attributes per method.

In the example below, I've included two "InlineData" attributes. Each "InlineData" attribute has an array with three integers. Each of these integers represent the parameters for the test method in ascending order.

// Test1.cs
public class Test1 : IDisposable
{
	public Test1()
	{
		// I can run some code before the test runs
	}

	[Theory]
	[InlineData(3, 5, 7)]
	[InlineData(1, 6, 10)]
	public void TestPattern2(int i1, int i2, int i3)
	{
		Assert.True(i1 <= 3);
		Assert.True(i2 >= 4);
		Assert.True(i3 <= 10);
	}

	public bool IsPasswordValid(string password)
	{
		return password == "TheBeast";
	}

	public void Dispose()
	{
		// I can run some code after the test has finished
	}
}

You can use the "InlineData" attribute, or you can use the "MemberData" and "ClassData" attribute. The "MemberData" attribute allows you to return your parameter data from a method by returning an IEnumberable<object[]>. The "ClassData" does the same as "MemberData", but you can return your data in a seperate class and inherit the IEnumberable<object[]>

public class Test1 : IDisposable
{
	public Test1()
	{
		// I can run some code before the test runs
	}

	[Theory]
	[MemberData(nameof(TestData))]
	public void TestPattern2(int i1, int i2, int i3)
	{
		Assert.True(i1 <= 3);
		Assert.True(i2 >= 4);
		Assert.True(i3 <= 10);
	}

	[Theory]
	[ClassData(typeof(ClassTestData))]
	public void TestPattern3(int i1, int i2, int i3)
	{
		Assert.True(i1 <= 4);
		Assert.True(i2 == 6);
		Assert.True(i3 == 8);
	}

	public bool IsPasswordValid(string password)
	{
		return password == "TheBeast";
	}

	public static IEnumerable<object[]> TestData()
	{
		return new List<object[]>
		{
			new object[] { 3, 5, 7 },
			new object[] { 1, 6, 10 }
		};
	}

	public void Dispose()
	{
		// I can run some code after the test has finished
	}
}

public class ClassTestData : IEnumerable<object[]>
{
	public IEnumerator<object[]> GetEnumerator()
	{
		yield return new object[] { 2, 6, 8 };
		yield return new object[] { 3, 6, 8 };

	}

	IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

More to come

These are just some of the basics for XUnit. As I start to use the unit testing framework more, I will fill you in on some of the features it has.