Cover Photo by Max Nelson on Unsplash
I’ve worked with MSTest and NUnit previously, but for whatever reason not with xUnit. Thankfully, coming from either framework seemed to translate pretty easily into xUnit.
In the recent post I did:
Your favorite/most useful extension methods?
I explored working with xUnit for the first time, to help to ensure the extension methods I was implementing were working as I intended. Though I had written previously about the extension methods, I hadn’t covered the testing of them; I thought that deserved its own post.
What is unit testing?
If you’ve never worked with unit testing before, wikipedia describes them as:
In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use.[1]
The way I usually describe it is: unit testing is the process of ensuring expected outcomes from a piece of code — ideally a single function — without relying on external resources. When unit testing, anything that’s needed outside of the method under test should be provided via an abstraction, rather than concretion — to ensure that you are testing only the method under test. The dependencies needed by the method can be provided by making use of mocks, fakes, and/or stubs — pieces of code programmed to exhibit specific behavior to help cover all potential “branches” within your code.
MSTest, nUnit, and xUnit differences
Not having worked with MSTest since prior to it allowing multiple testing scenarios to a single test, I wasn’t sure how to describe the differences. Luckily, a quick google search pointed me to a “differences” page written up on the xUnit site:
Comparing xUnit.net to other frameworks > xUnit.net
It appears that all the frameworks do the same thing more or less; though I find it interesting that .net core seems to pretty extensively use xUnit over their own testing framework of MSTest.
Project Setup
Thankfully, there is a project template (at least with whatever VS install options I used) to create an xUnit test project. If you do not have this, you just need to add the following NuGet packages to a project:
- Microsoft.NET.Test.Sdk
- xunit
- xunit.runner.visualstudio
Your csproj file should look like this(ish):
Testing a method
I picked a method from my extension methods post, that would allow me to make use of various types of using xUnit:
- Fact — this piece of code does [this] thing
- Theory — this piece of code does [this] thing, and here are multiple examples of that occurring with separate data driven tests.
- Expected Exception — this type of test is checking the code throws an exception of an expected type.
The method we’ll be testing with is:
/// <summary>
/// Attempts to find the first item in <see cref="items"/>
/// meeting <see cref="Predicate{T}"/>.
///
/// Returns the <see cref="true"/> when item found,
/// <see cref="false"/> when not.
///
/// Found result is contained within out parameter <see cref="result"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="predicate"></param>
/// <param name="result"></param>
/// <returns><see cref="true"/> when item found. <see cref="false"/> otherwise.</returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <see cref="items"/> or <see cref="Predicate{T}"/> is null.
/// </exception>
public static bool TryFirst<T>(
this IEnumerable<T> items,
Func<T, bool> predicate,
out T result
)
{
if (items == null)
throw new ArgumentNullException(nameof(items));
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
result = default(T);
foreach (var item in items)
{
if (predicate(item))
{
result = item;
return true;
}
}
return false;
}
The above extension method exists on IEnumerable<T>
and works very similar to built in methods like int.TryParse(...)
. This method returns true/false depending on whether or not an item is found based on the predicate, and that item is then contained within the out parameter of result.
How can we test this? What in this method can be tested? Here are some of the things that can be tested:
-
ArgumentNullException
ifitems
isnull
. -
ArgumentNullException
ifpredicate
isnull
-
false
when no item is found matching thepredicate
-
true
when anitem
is found matching thepredicate
-
result
contains the founditem
that met thepredicate
Test class setup
Let’s start with our test class:
public class EnumerableExtensionTests
{
}
One thing I like about xUnit over nUnit, is that I don’t need to decorate the class in any way to indicate it is a class with unit tests. We’ll need (or rather, I’d like) a class to play around with, since our extension method works on a generic IEnumerable<T>
. We can introduce a little fake class for testing purposes by updating our test class to:
public class EnumerableExtensionTests
{
public class EnumerableTest
{
public int SomeInt { get; set; }
}
public static List<object[]> EnumerableTestData =>
new List<object[]>()
{
new object[]
{
new List<EnumerableTest>()
{
new EnumerableTest() { SomeInt = 1 },
new EnumerableTest() { SomeInt = 42 },
}
}
};
}
In the above, I’m defining a new type, to be used only by this test class, that has a property of SomeInt
. Additionally, I set up some static data we will be using shortly to help ensure our method is working properly.
Let’s get into the tests
We have two ways our extension method can throw an ArgumentNullException
— when either items or predicate
is null
. Let’s see what tests for those conditions can look like.
-
ArgumentNullException
ifitems
is null.
[Fact]
public void ShouldThrowWhenItemsNull()
{
List<EnumerableTest> items = null;
Assert.Throws<ArgumentNullException>(() =>
items.TryFirst(t => t.SomeInt == 42, out var result)
);
}
In the above, we’re setting up our IEnumerable<T>
to be null
, covering one of our branches in our method — this tests that when the items are null
, an ArgumentNullException
is thrown. NUnit decorated tests as [Test]
, here the test (in xUnit) is decorated as [Fact]
.
-
ArgumentNullException
ifpredicate
isnull
[Theory]
[MemberData(nameof(EnumerableTestData))]
public void ShouldThrowWhenPredicateNull(List<EnumerableTest> items)
{
Assert.Throws<ArgumentNullException>(() =>
items.TryFirst(null, out var result)
);
}
In the above, I’m using a [Theory]
rather than [Fact]
— I’m not sure that I have to in this case, but from my NUnit days, I couldn’t instantiate instances of non primitives within an attribute, though I’m not sure if that’s a limitation of NUnit, or c#. Anyway, in this test I’m providing the sample data to the method that was defined in the test class definition, and then providing a null
predicate
; the second condition for throwing a ArgumentNullException
.
-
false
when an noitem
is found meeting thepredicate
In this test, we want to provide a valid IEnumerable<T>
to the extension method, with a valid predicate
; though in this case our predicate
is not a match on any item
within items
.
[Theory]
[MemberData(nameof(EnumerableTestData))]
public void ShouldReturnFalseWhenDataNotFound(List<EnumerableTest> items)
{
var found = items.TryFirst(
t => t.SomeInt == 100, out var result
);
Assert.False(found);
}
In the above, we’re making sure that the boolean
returned from our function is false
, since 100 does not exist within our sample data.
-
true
when anitem
is found meeting thepredicate
Next, we’ll make sure that the result of the extension method is true
when the predicate
matches an item
within the items
.
[Theory]
[MemberData(nameof(EnumerableTestData))]
public void ShouldReturnTrueWhenDataFound(List<EnumerableTest> items)
{
var found = items.TryFirst(
t => t.SomeInt == 42, out var result
);
Assert.True(found);
}
The above is really similar to the false
test, except we’re providing a valid predicate
that does match an item
within items
.
-
result
contains the founditem
that met thepredicate
Our final test, ensures that we retrieve the item
in the out results parameter from the function:
[Theory]
[MemberData(nameof(EnumerableTestData))]
public void ShouldReturnDataWhenDataFound(List<EnumerableTest> items)
{
var dataToSearch = 42;
var found = items.TryFirst(
t => t.SomeInt == dataToSearch, out var result
);
Assert.Equal(dataToSearch, result.SomeInt);
}
It looks like this when running our tests:
xUnit and nUnit seem to be pretty similar in syntax and structure, though I do enjoy the notion of using constructors for test class setup, rather than SetUp
as with nUnit.
The code from this post can be found:
Kritner-Blogs/ExtensionMethods
Related:
Top comments (0)