Unit testing .Net Framework/.Net Core using xUnit
Introduction
xUnit.net is a free, open source,
community-focused unit testing tool for the .NET Framework. xUnit.net is the latest technology for unit
testing C#, F#, VB.NET and other .NET languages. xUnit.net works with ReSharper, CodeRush,
TestDriven.NET and Xamarin.
If you’re developing a .NET-based
application, Microsoft provides MSTest framework that has excellent integration
with Visual Studio. When it’s time to execute these unit tests on a build
engine, the MSTest execution engine requires an installation of Visual Studio
to be available on the build server. You can choose any build server, but
Visual Studio must be installed on it. You won’t always have complete access to
the build server to install the tools that you need. Here, xUnit comes handy!
Advantages of xUnit over MSTest
The xUnit tool has gained popularity over MSTest for following reasons:
- It provides support for parameterized tests using the Theory attribute whereas MSTest doesn’t provide such a feature out of the box.
- Tests in xUnit don’t require a separate Visual Studio Test Project as mandated by MSTest.
- Prioritizing or sequencing xUnit tests is simple (with the TestPriority attribute) unlike in MSTest framework, which requires a unit test settings file.
- It supports fluent assertions like Assert.Throws<> instead of ExpectedException attribute in MSTest framework.
- It can be referenced in any Visual Studio Project as a NuGet package.
- Unlike MSTest, xUnit doesn’t require any additional tools to be installed on the build server.
Writing test cases using xUnit
In this section we will demonstrate how
we can use xUnit for .Net Framework and write some basic test cases and run
those test cases in console mode and visual studio. We will also explore
options to initialize the context and share the context between test cases.
Basic test
Create a new class library project in
Visual studio, File->New->Project→Class Library(.Net Framework). Make
sure you select the .Net framework version 4.5.2 and above. Once the project is
created, right click on the project and choose Manage Nuget Packages. Browse
for the package 'xunit' and
install the same to your project.
Now add a new class such as 'BasicTest.cs' and add the following code snippet
Build the code and ensure there are no
errors.
Fact is an attribute which indicates
the the method to be tested. xUnit uses the Fact attribute to locate the method
and executes through reflection. Note, we don't declare any attribute to define
the calls is a test class. xUnit minimizes the use of attributes.
Running test using console
Now that we have the test project ready
we can execute the test cases using console mode. To run the project in
console, we need to add a nuget library 'xunit.runner.console'. Include the
library from nuget package manager as described above. Once it is added rebuild
the project.
To execute the test cases open the
command prompt(to the solution folder) and execute the following command, 'packages\xunit.runner.console.2.4.1\tools\net472\xunit.console
xUnitTestingDemo\bin\Debug\xUnitTestingDemo.dll'.
Note: Substitute the project name of
yours instead of 'xUnitTestingDemo'.
You will see the similar output as shown
below,
Running test using visual studio
Install the package 'xunit.runner.visualstudio'
from nuget package manager as described above. Every time when you build a
project visual studio will automatically detect the test cases. All the test
cases are visible in 'Test
Explorer' window.
You will see the test cases as shown
below,
Test Initialization and context sharing
It is common for unit test classes to
share setup and cleanup code (often called "test context"). xUnit.net offers the following methods for
sharing this setup and cleanup code.
Constructor and Dispose
Constructor and dispose will be used
when you want a clean test context for every test (sharing the setup and
cleanup code, without sharing the object instance).
xUnit creates a new instance of the test
class for every test that is run, so any code which is placed into the
constructor of the test class will be run for every single test. This makes the
constructor a convenient place to put reusable context setup code where you
want to share the code without sharing object instances.
Here is a sample example,
Unlike MSTest we don't declare the
'TestInitialize' separately. Since the constructor is the class initialization,
xUnit uses the power of constructor and initializes the context. Similarly the
dispose method will clean up all the resources.
Class Fixtures
Class fixtures are used when you want to
create a single test context and share it among all the tests in the class, and
have it cleaned up after all the tests in the class have finished.
Sometimes test context creation and
cleanup can be very expensive. If you were to run the creation and cleanup code
during every test, it might make the tests slower than you want. You can use
the class fixture feature of xUnit to share a
single object instance among all tests in a test class. This means the context
is created only once for a class instead of each test cases seperately.
To use class fixtures, you need to take
the following steps:
· Create the fixture class, and put the startup code in the fixture class constructor.
· If the fixture class needs to perform cleanup, implement
IDisposableon the fixture class, and put the cleanup code in theDispose()method.· Add
IClassFixture<>to the test class.· If the test class needs access to the fixture instance, add it as a constructor argument, and it will be provided automatically.
Here is a sample example,
Collection fixtures when you want to
create a single test context and share it among tests in several test classes,
and have it cleaned up after all the tests in the test classes have finished.
Sometimes you will want to share a
fixture object among multiple test classes. For example, you may want to
initialize a database with a set of test data, and then leave that test data in
place for use by multiple test classes.
To use collection fixtures, you need to
take the following steps:
· Create the fixture class, and put the startup code in the fixture class constructor.
· If the fixture class needs to perform cleanup, implement
IDisposableon the fixture class, and put the cleanup code in theDispose()method.
· Create the collection definition class, decorating it with the
[CollectionDefinition]attribute, giving it a unique name that will identify the test collection.· Add
ICollectionFixture<>to the collection definition class.· Add the
[Collection]attribute to all the test classes that will be part of the collection, using the unique name you provided to the test collection definition class's[CollectionDefinition]attribute.· If the test classes need access to the fixture instance, add it as a constructor argument, and it will be provided automatically.
Here is an example,
xUnit treats collection fixtures in much
the same way as class fixtures, except that the lifetime of a collection
fixture object is longer: it is created before any tests are run in any of the
test classes in the collection, and will not be cleaned up until all test
classes in the collection have finished running.
Working with xUnit Theory
Theories are tests which are only
true for a particular set of data. xUnit Theory depends on set of
parameters and its data, our test will pass for some set of data and not the
others. We have a theory which postulate that with this set of data, this will
happen.
There are three ways we can formulate a
theory.
Inline Data
This is a simplest form of testing our
theory with data. Let us see an example,
As you see above, we provide some values
in InlineData and xUnit will create two tests
and every time populates the test case arguments with what we’ve passed
into InlineData.
Class Data
ClassData is another attribute that we can
use with our theory, with ClassData we have more flexibility and less
clutter
Here we have created a class that
inherits from IEnumerable<object[]>, note that it has to be an object,
otherwise xUnit will throws an error. Next we create a private list of object
that intend to pass to our theory and finally we implemented the GetEnumerator method with piggybacking on our list Enumerator.
Now we can pass our TestDataGenerator class to ClassData attribute and
the returned data form that class will populate the test case’s parameters.
Member Data
MemberData gives us the same flexibility but
without the need for a class.
The [MemberData] attribute
can load data from an IEnnumerable<object[]> property on the test class. The
xUnit analyzers will pick up any issues with your configuration, such as
missing properties, or using properties that return invalid types.
Handling exception in xUnit
When writing tests it is sometimes
useful to check that the correct exceptions are thrown at the expected time.
Let look at an example,
The first test case is the happy path,
where the test case evaluate to correct value. The second test case, expected
exception is tested using, Assert.Throws<> and exception type as
generic parameter. The third test case captures the exception in a variable for
further assertions.
References
To get more documentation and resources on xUnit, refer the following websites,
- xUnit home page: https://xunit.net/
- Github: https://github.com/xunit/xunit

Comments
Post a Comment