xUnit.net: Global setup + teardown?
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.