How to perform Unit testing with xUnit?


When we talk about unit testing in .NET core applications, xUnit is undoubtedly the preferred library of most programmers. However, in addition to applying good testing practices, it is necessary to know and take advantage of the available resources of the tools so that your tests are clean and easily maintainable.

.Net core vs .Net framework

The .NET Framework has historically worked only on mobile platforms. The Xamarin and Mono projects have been working to bring .NET to Linux, macOS, and mobile devices. A simple theoretical library which can now be seen throughout Windows, Linux, macOS, and mobile devices (via Xamarin) is provided by .NET Core Microservices.

Microservices with .net core

Relatively small, customizable, and independently deployable services are microservices. Docker containers (for Linux and Windows) simplify deployment and testing by bundling a single unit with service and its dependencies, which is then run in an isolated environment.

.Net core latest version

.NET Core for Windows, Linux, and macOS operating systems is a free and open-source, managed computer software framework. It is a successor to the cross-platform .NET Framework. The project is mainly developed and released under the MIT License by Microsoft employees through the .NET Foundation.

In this blog, we will cover the main features of xUnit.

Facts

To start chatting about xUnit features, nothing better than talking about your most used feature, Fact.

As the name itself makes apparent, the idea is to test a fact, something that is “unique” and objective. This is the most widely used feature in the library and can easily meet all your needs in a unit test project.

how to perform unit testing with xUnit

With this structure, it is possible to do any type of test. However, when the “logic” of the test needs to be the same by modifying only the input parameters, the duplication of the test code is inevitable. Obviously, in some cases, “duplication” may be acceptable and might make sense, but for the most part, it makes no sense at all.

Theory about (Unit testing)xUnit

To resolve the previously presented problem, xUnit has a feature called Theory, in which the logic is the same for a set of different inputs and outputs. Rewriting the previous tests would look something like:

how to perform unit testing with xUnit

This code would run twice, once for each InlineDate of the annotation, passing its respective parameters. In the test results, it is subdivided into the number of tests that are run allowing you to view which option failed. Using Theory allows you to reuse your test code for several different inputs/outputs.

A great option to make it easier to maintain tests on eventual changes.

In some cases, your InlineData may get too large by polluting the tests. Or, you may need to pass objects as a parameter. But as we know, you can’t instantiate them and pass them via method annotation.

To resolve this issue, let’s exemplify the use of member data.

how to perform unit testing with xUnit

In this slightly larger example, we were able to use objects as parameters and for this, we need to create an IEnumerable<object[]> to provide the necessary inputs. Simply put, it is a list of objects (which will be inputted into the test method) within another list (test cases).

It might seem complex but the idea is to prefer objects to many parameters in the signature of a method in our architecture. Given this fact, using this structure may be the best option.

If your test is getting too long, you can choose to put one more attribute, ClassData, which is very similar to MemberData, but you get a class that inherits from IEnumerable<object[]>, so it’s easier to isolate the “test case templates” from the implementations themselves.

Finally, we can also use the “hybrid” form. A merge between MemberDataand ClassData (but it is purely MemberData separating the parameters into an external hehe class).

In this post, we saw that in addition to Fact it is possible to reuse a lot of the code and logic of the tests with the use of Theory and its possible forms of data input.

Do not forget that tests should also be elegant and follow the principles of clean code, after all.

If you have your entire system tested and made any modifications, at least one test should break and you should adjust it to its modification, to ensure that the behaviour of the system tests all possibilities and fails when minimal changes are made.