Test lifecycle
Integration tests often need to prepare things before the test and clean up after. For instance, database integration tests are possible, and desirable, but database initialization is too slow to do in each test. MsTest is actually easier to work with than NUnit (which is close) and XUnit (which is better in v2 than v1).
MsTest lifecycle
You can hook into various events before and after tests. Some require specific signatures (static with TestObject argument)
//all tests in project (only 1 per project)
[AssemblyInitialize]
public static void AssemblyInit(TestContext context) { }
[AssemblyCleanup]
public static void AssemblyCleanup() { }
//when class created. Or just use constructor.
[ClassInitialize]
public static void ClassInit(TestContext context) { }
[ClassCleanup]
public static void ClassCleanup() { }
//before each test in class
[TestInitialize]
public void Initialize() { }
[TestCleanup]
public void Cleanup() { }
[TestMethod]
public void MyActualTest() { }
NUnit life cycle
NUnit has similar capabilities without the odd signatures. There's no AssemblyInitialize, but you can do a class with SetUpFixture attribute for a specific namespace.
[SetUpFixture] //called for namespace- not assembly!
public class NamespaceInit
{
[SetUp]
public void SetupNamespace() { }
[TearDown]
public void TearDownNamespace() { }
}
[TestFixture]
public class NUnitLifeCycleTests
{
//called when this class created/ended
[TestFixtureSetUp]
public void Init() { }
[TestFixtureTearDown]
public void Cleanup() { }
//called before each test
[SetUp]
public void Setup() { }
[TearDown]
public void TearDown() { }
//the test
[Test]
public void TestMethod1() { }
}
XUnit lifecycle
XUnit does not have Setup/Teardown attributes, but you can use constructors and disposables. For some things, derive from the BeforeAfterTestAttribute.
[assembly: CollectionBehavior(DisableTestParallelization = true)]
public class XUnitTests : IDisposable
{
public XUnitTests() { } //use default ctor as ClassInitialize
public void Dispose() { } //use Disposable for ClassCleanUp
[Fact]
[ResetDatabase]
public void TestMethod() { }
internal class ResetDatabaseAttribute : Xunit.Sdk.BeforeAfterTestAttribute
{
//could be used to restore/rollback database
//NB: not compatible with parallelism
public override void Before(MethodInfo methodUnderTest) { }
public override void After(MethodInfo methodUnderTest) { }
}
}
In XUnit v1 there's an IUseFixture<T> where T can be shared between methods.
public class MoreTests : IUseFixture<DataContext>
{
private DataContext _context;
public void SetFixture(DataContext context)
{
_context = context;
}
[Fact]
public void TestMethod()
{
Assert.NotNull(_context.Products.First());
}
}
In XUnit v2 (2015+) there's an IClassFixture (class based), which now uses constructor injection.
public class MoreTests : IClassFixture<DataContext>
{
private readonly DataContext _context;
public MoreTests(DataContext context)
{
_context = context;
}
[Fact]
public void TestMethod()
{
Assert.NotNull(_context.Products.First());
}
}
In XUnit v2 (2015+) there a separate ICollectionFixture, which is more suited to database tests. The test classes get a [Collection("name")] attribute. For config, you create an unused class with [CollectionDefinition("name")] and derived from :ICollectionFixture<DataContext>
At which point, you may prefer MsTest's AssemblyInitialize...