xUnit.net offers several methods for sharing this setup and cleanup code, depending on the scope of things to be shared, as well as the expense associated with the setup and cleanup code.
As far as NUnit vs. XUnit vs. MSTest is concerned, the biggest difference between xUnit and the other two test frameworks (NUnit and MSTest) is that xUnit is much more extensible when compared to NUnit and MSTest. The [Fact] attribute is used instead of the [Test] attribute.
test fixture (in xUnit) In xUnit, a test fixture is all the things we need to have in place in order to run a test and expect a particular outcome. Some people call this the test context.
Setup methods (both types) are called on base classes first, then on derived classes. If any setup method throws an exception, no further setups are called. Teardown methods (again, both types) are called on derived classes first, then on the base class.
As far as I know, xUnit does not have a global initialization/teardown extension point. However, it is easy to create one. Just create a base test class that implements IDisposable
and do your initialization in the constructor and your teardown in the IDisposable.Dispose
method. This would look like this:
public abstract class TestsBase : IDisposable
{
protected TestsBase()
{
// Do "global" initialization here; Called before every test method.
}
public void Dispose()
{
// Do "global" teardown here; Called after every test method.
}
}
public class DummyTests : TestsBase
{
// Add test methods
}
However, the base class setup and teardown code will be executed for each call. This might not be what you want, as it is not very efficient. A more optimized version would use the IClassFixture<T>
interface to ensure that the global initialization/teardown functionality is only called once. For this version, you don't extends a base class from your test class but implement the IClassFixture<T>
interface where T
refers to your fixture class:
using Xunit;
public class TestsFixture : IDisposable
{
public TestsFixture ()
{
// Do "global" initialization here; Only called once.
}
public void Dispose()
{
// Do "global" teardown here; Only called once.
}
}
public class DummyTests : IClassFixture<TestsFixture>
{
public DummyTests(TestsFixture data)
{
}
}
This will result in the constructor of TestsFixture
only being run once
for every class under test. It thus depends on what you want exactly to choose between the two methods.
I was looking for the same answer, and at this time the xUnit documentation is very helpful in regards to how to implement Class Fixtures and Collection Fixtures that give developers a wide range of setup/teardown functionality at the class or group of classes level. This is in line with the answer from Geir Sagberg, and gives good skeleton implementation to illustrate what it should look like.
https://xunit.net/docs/shared-context
Collection Fixtures 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.
Sometimes you will want to share a fixture object among multiple test classes. The database example used for class fixtures is a great 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. You can use the collection fixture feature of xUnit.net to share a single object instance among tests in several test class.
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 IDisposable on the fixture class, and put the cleanup code in the Dispose() 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 a simple example:
public class DatabaseFixture : IDisposable
{
public DatabaseFixture()
{
Db = new SqlConnection("MyConnectionString");
// ... initialize data in the test database ...
}
public void Dispose()
{
// ... clean up test data from the database ...
}
public SqlConnection Db { get; private set; }
}
[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
[Collection("Database collection")]
public class DatabaseTestClass1
{
DatabaseFixture fixture;
public DatabaseTestClass1(DatabaseFixture fixture)
{
this.fixture = fixture;
}
}
[Collection("Database collection")]
public class DatabaseTestClass2
{
// ...
}
xUnit.net 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.
Test collections can also be decorated with IClassFixture<>. xUnit.net treats this as though each individual test class in the test collection were decorated with the class fixture.
Test collections also influence the way xUnit.net runs tests when running them in parallel. For more information, see Running Tests in Parallel.
Important note: Fixtures must be in the same assembly as the test that uses them.
There is an easy easy solution. Use the Fody.ModuleInit plugin
https://github.com/Fody/ModuleInit
It's a nuget package and when you install it it adds a new file called ModuleInitializer.cs
to the project. There is one static method in here that gets weaved into the assembly after build and is run as soon as the assembly is loaded and before anything is run.
I use this to unlock the software license to a library that I have purchased. I was always forgetting to unlock the license in each test and even forgetting to derive the test from a base class which would unlock it. The bright sparks that wrote this library, instead of telling you it was license locked introduced subtle numerical errors which causes tests to fail or pass when they shouldn't. You would never know if you had correctly unlocked the library or not. So now my module init looks like
/// <summary>
/// Used by the ModuleInit. All code inside the Initialize method is ran as soon as the assembly is loaded.
/// </summary>
public static class ModuleInitializer
{
/// <summary>
/// Initializes the module.
/// </summary>
public static void Initialize()
{
SomeLibrary.LicenceUtility.Unlock("XXXX-XXXX-XXXX-XXXX-XXXX");
}
}
and all tests that are placed into this assembly will have the license unlocked correctly for them.
To share SetUp/TearDown-code between multiple classes, you can use xUnit's CollectionFixture.
Quote:
To use collection fixtures, you need to take the following steps:
- Create the fixture class, and put the the startup code in the fixture class constructor.
- If the fixture class needs to perform cleanup, implement IDisposable on the fixture class, and put the cleanup code in the Dispose() 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.
If you have one global initialization and global cleanup functions, you can write class like this:
[CollectionDefinition("TestEngine")]
public class TestEngineInitializer: IDisposable, ICollectionFixture<TestEngineInitializer>
{
public TestEngineInitializer()
{
MyOwnTestEngine.Init();
}
public void Dispose()
{
MyOwnTestEngine.Cleanup();
}
}
And for each test class, where this initialization is required to be executed, you need to add extra attribute:
[Collection("TestEngine")]
public class MyTests
{
Important: Names used in Collection
and CollectionDefinition
-attributes must match.
You can use provide also TestEngine class instance into constructor, for example like this:
[Collection("TestEngine")]
public class MyTests
{
public MyTests(TestEngineInitializer initializer)
{
}
but this is not mandatory.
Whole documentation on issue is located in here:
https://xunit.net/docs/shared-context
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With