xUnit Best Practices¶
The basics¶
Factare tests which are always true. They test invariant conditions. runnerTheoryTheories are tests which are only true for a particular set of data.
Unit test structure using Fact attribute:
| C# | |
|---|---|
In addition to the Fact attribute, you can also use the Theory attribute on
test methods. Theory runs a test method multiple times, passing different data
values each time. You have a variety of tools for setting the data values to be
passed to your test method.
Unit test structure using Theory attribute:
| C# | |
|---|---|
Using InlineData is easy and straightforward, by there are some limitations,
because being an attribute we can't create object instances. We may use other
sources in such scenarios. Source data for Theory unit tests can be provided
in may ways:
- Using
InlineData: it's clear and useful, but limited. - Using
MemberData: points to an staticproperty,fieldormethodto feedTheory's data source. - Using
ClassData: uses a class to feedTheory's data source. It can be an static class or extendTheoryDataprovided by xUnit.
Bear in mind that each Theory with its data set is considered to be a separate
test. In the test results outcome, the runner tells you exactly which set of
data failed, because it includes the parameter values in the name of the test.
Note
Prefer InlineData when possible over the other alternatives because it's
more comprehensible for the developer, and it offers a bonus point:
allow us to differentiate each execution instance provided by individuals
InlineData entry in the Test explorer window.
Put tests in assemblies apart from production code¶
The test must be placed in assemblies apart from code. Tests must never be shipped to production environments. Sometimes they requires dependencies that we don't want in final deployments. By separating test assemblies from production code we will be able to manage the dependencies as we need without make footprints in the production code.
Place related unit tests in individual files¶
Each component being tested should be placed in its own file, keep those tests next each other to facilitate its localization.
Use Skip attribute for temporarily disabled tests¶
Sometimes you need to disable some tests for some reason, the best way to make
the tests don't run for a while is to mark them as skipped using the Skip
attribute. Don't comment out the failing test because that way you won't realize
which of them needs to be worked out to bring them alive again, use Skip
attribute instead.
By using Skip attribute you'll be able to clearly identify those skipped tests
among failed and succeeded ones, and due to the fact that the attribute requires
a reason as mandatory, you will get enough information to act on.
| C# | |
|---|---|
Parallelism in xUnit¶
How does xUnit.net decide which tests can run against each other in parallel? It
uses a concept called test collections to make that decision.
By default, each test class is a unique test collection. Tests within the same
test class will not run in parallel against each other. It means that, by
default, xUnit runs tests in different test classes in parallel, which can
significantly shorten the time to run all your tests. It also means that xUnit
effectively ignores the Run Tests in Parallel setting at the top of the Test
Explorer window.
You can, however, override this default behavior where you need tests in
different classes to run sequentially by assigning test classes to the same
collection. You assign tests to a collection using the Collection attribute,
passing a name for the collection.
| C# | |
|---|---|
By using the same test Collection attribute we can tell the TestRunner to
run methods in different classes to run sequentially.
There are more advanced options that can be used to particularize your test fixtures, that can be seen here.
Sharing context¶
Test context can be shared by three main mechanisms:
ConstructorandDispose(shared setup/cleanup code without sharing object instances)Class Fixtures(shared object instance across tests in a single class)Collection Fixtures(shared object instances across multiple test classes)
Constructor and Dispose¶
xUnit.net 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.
When to use: when you want a clean test context for every test (sharing the setup and cleanup code, without sharing the object instance).
Remember to implement IDisposable pattern to free up any resource allocated.
Class fixtures¶
When to use: 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.
When using a class fixture, xUnit will ensure that the fixture instance will
be created before any of the tests have run, and once all the tests have
finished, it will clean up the fixture object by calling Dispose, if present.
Collection Fixtures¶
Sometimes you will want to share a fixture object among multiple test classes.
You can use the collection fixture feature of xUnit to share a single object
instance among tests in several test class.
When to use: 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.
Please follow up this link for further reading.
Comparing xUnit with NUnit¶
These are some major changes made during the evolution from NUnit to xUnit:
- Single Object Instance per Test Method.
- No
SetUporTearDownmethods available, - No
ExpectedException, useAssert.Throwsinstead. TestFixturewas removed entirely; tests can be in any public class.Ignoreis expressed using theSkip= parameter onFactorTheory.SetUpandTearDownare removed in favor ofConstructorandIDisposable.TestFixtureSetupandTestFixtureTearDownare removed in favor of implementing reusable fixture data classes, which are attached to test classes by having them implementIUseFixture<T>.
A more detailed comparison of xUnit to other frameworks can be seen
here.