HowTo – XUnit

Source Code

All source code can be found on GitHub here.

My cheat sheet for XUnit can be found here.

This is part of my HowTo in .NET seriies. An overview can be seen here,

Intro

XUnit is a unit test framework. The features appear to be somewhat lacking when compared to NUnit but it aims to only provide the functionality which is intended to be common and supports good unit testing structure. For example in NUnit [TestFixureSetUp] does not promote test isolation so an equivalent functionality have been purposefully omitted.

There is also no fluent assertions but there are 3rd party dlls available instead.

XUnit also differs from other unit test frameworks by not relying on attributes as much. For example constructor and dispose methods are used for test set up and tear down functions.

Basic Test Creation

The following examples consists of a simple class with a parameter-less constructor that implements IDisposable. There is also one public method decorated with [Fact] attribute.

The constructor is our test setup function.

The Dispose is our test tear down function.

The Method with [Fact] is our test.

public class AccountTests : IDisposable		// Allows Test TearDown via Dispose()
{

	public AccountTests()
	{
	}

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

	public void Dispose()
	{
	}

}

IUseFixture

Common set up code can be refactored out to a helper class and injected in automatically via the IUseFixture interface and implementing its only method SetFixture(T t)

public class AccountTests : IUseFixture<AccountTests.TestSetUpClass> // Allows Test Set Up via injected class
{

	public void SetFixture(TestSetUpClass setupClass)
	{
	    // Can inject a class to provide any set up here.
	    setupClass.AnyFunction();
	}

	public class TestSetUpClass
	{
	    public void AnyFunction()
	    {
		// Set up code goes in here.
	    }
	}
}

InLineData allows multiple traversals of the same test with different parameters. It can take any number and types of parameters though they have to match the method signature which they are decorating.

The following example will result in 3 tests with the parameters, {1, true}, {2, true}, {3, false}

[Theory,
InlineData(1, true),
InlineData(2, true),
InlineData(3, false)]
public void InlineDataExample(int number, bool expectedResult)
{
    Console.WriteLine("AccountTests.DemoSimpleTestWithInLineData(int {0}, bool {1})", number, expectedResult);
    Assert.Equal(number < 3, expectedResult);
}

PropertyData

Similar to InLineData though the test data can be injected in from a property.

The property should returns IEnumerable where the object array contains the same matching parameters to the method signature of the test method.

public static IEnumerable<object[]> SamplePropertyDataProperty
{
    get
    {
        yield return new object[] { 1, true };
        yield return new object[] { 2, true };
        yield return new object[] { 3, false };
    }
}

[Theory]
[PropertyData("SamplePropertyDataProperty")]
public void PropertyDataExample(int number, bool expectedResult)
{
    Console.WriteLine("AccountTests.PropertyDataExample(int {0}, bool {1})", number, expectedResult);
    Assert.Equal(number < 3, expectedResult);
}

Assertions

Assertions are conditions which will cause our test to fail or pass. Each assertion will cause an exception to be thrown when the condition is not met. The test runner will handle the exception, fail the test and start running the next test. If a unit test runs to the end without an exception then the test is considered as passed.

If an exception is thrown which is not part of an Assert but inside the test it is considered as failed.

If an exception is thrown inside the code under test which is not expected the test is considered as failed. If an exception being thrown is the expected result then special exception asserts must be used.

Contains asserts that something is found within a reference type which implements IEnumerable or IEnumerable ( collection ).

A string is a collection of chars and implements IEnumerable.

Assert.Contains("n", "FNZ", StringComparison.CurrentCultureIgnoreCase);
Assert.Contains("a", new List<String> { "A", "B" }, StringComparer.CurrentCultureIgnoreCase);

Does not contain ensures that something is not within a collection or IEnumerable.

Assert.DoesNotContain("n", "FNZ", StringComparison.CurrentCulture);
Assert.DoesNotContain("a", new List<String> { "A", "B" }, StringComparer.CurrentCulture);

Empty and not empty ensures that a collection of IEnumerable is or is not empty.

Assert.Empty(new List<String>());
Assert.NotEmpty(new List<String> { "A", "B" });

Equality checks with the ability to pass in a IComparer and also a precision for number of dp; equality up to x decimal places.

The ability to compare all elements within a collection against another collection is also catered for with an overloaded method for IComparer

Assert.Equal(1, 1);
Assert.Equal(1.13, 1.12, 1); // Precsions Num DP
Assert.Equal(new List<String> { "A", "B" }, new List<String> { "a", "b" }, StringComparer.CurrentCultureIgnoreCase);

Assert.NotEqual(1, 2);
Assert.NotEqual(new List<String> { "A", "B" }, new List<String> { "a", "b" }, StringComparer.CurrentCulture);

Conditions for asserting boolean logic, is and is not null.

Assert.False(false);
Assert.NotNull(false);
Assert.Null(null);
Assert.True(true);

Range comparer ensures that a value is greater than and equal to a number and also less than or equal to another.

Assert.InRange(1, 0, 10);
Assert.NotInRange(-1, 0, 10);

Assertions to ensure a memory allocation is of a required type.

Assert.IsType(Type.GetType("System.Int32"), 1);
Assert.IsNotType(Type.GetType("System.Double"), 1);

Identity ( is the same object ) assertions are performed with Same and NotSame functions.

var foo = new object();
var moo = new object();
Assert.Same(foo, foo);
Assert.NotSame(foo, moo);

Exceptions can be tested for with the Throws function. The code under test needs to be provided inside a delegate otherwise the exception is not expected and will cause the unit test to fail.

Assert.Throws<Exception>(() => { throw new Exception(); });

Custom IEqualityComparer

Custom comparers can be written to provide bespoke equality comparison. This is not part of XUnit though we can use this functionality with our assertions.

In the example below we have a class called Foo which has an Id and a Name. Id is an int and Name is a string. We want equality to be based upon the Id and the Name case insensitive.

We create a class implementing IEqualityComparer and implement the Equals and GetHasCode functions. Equals is our main function here as this is what our unit test will be using for the equals assertions.

public class Foo
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class FooComparer : IEqualityComparer<Foo>
{
    public bool Equals(Foo x, Foo y)
    {
        return x.ID == y.ID && x.Name.Equals(y.Name, StringComparison.CurrentCultureIgnoreCase);
    }

    public int GetHashCode(Foo obj)
    {
        return obj.ID.GetHashCode();
    }
}

We can pass in an instance of our custom comparer to the Assert.Equal

Assert.Equal(new Foo { ID = 1, Name = "A Name"), new Foo { ID = 1, Name = "a name"), new FooComparer());
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s