Overview
In xUnit.net, a new instance of the test class is created for every test method and destroyed once the test is complete. By utilizing this lifecycle, it is standard practice to write “Setup” in the constructor and “Teardown” in the Dispose method of the IDisposable interface.
Specifications (Lifecycle)
- Setup: Written in the constructor (
public ClassName() { ... }). It is called before every test method. - Teardown: Written in the
Dispose()method. It is called after every test method finishes. - Interface: The class must implement the
IDisposableinterface.
Basic Usage
public class MyTests : IDisposable
{
// Constructor: Setup
public MyTests()
{
// Initialize database connections or lists
}
// Dispose: Teardown
public void Dispose()
{
// Delete files or close connections
}
[Fact]
public void Test1() { /* ... */ }
}
Full Code
The following example shows how to initialize a Stack<int> as a setup process so that each test method uses a clean instance.
using System;
using System.Collections.Generic;
using Xunit;
namespace UnitTestPatterns
{
// Implement IDisposable to enable teardown processing
public class StackTests : IDisposable
{
private Stack<int> _stack;
/// <summary>
/// [Setup]
/// The constructor is called every time a test method (Test1, Test2) runs.
/// A new instance of _stack is created each time.
/// </summary>
public StackTests()
{
_stack = new Stack<int>();
Console.WriteLine("Setup: Stack initialized.");
}
/// <summary>
/// [Teardown]
/// This is called every time a test method finishes.
/// Use this to release resources.
/// </summary>
public void Dispose()
{
_stack = null; // Shown as an example
Console.WriteLine("Teardown: Resources released.");
}
[Fact]
public void Push_OneItem_CountIsOne()
{
// Arrange (Completed in the constructor)
// Act
_stack.Push(42);
var count = _stack.Count;
// Assert
Assert.Equal(1, count);
}
[Fact]
public void Pop_OneItem_ReturnsValueAndCountIsZero()
{
// Arrange
_stack.Push(11);
// Act
var val = _stack.Pop();
var count = _stack.Count;
// Assert
Assert.Equal(11, val); // The value should be 11
Assert.Equal(0, count); // The stack should be empty
}
}
}
Customization Points
- Class Fixtures (IClassFixture): While constructors run for every test method, use
IClassFixture<T>for heavy tasks (like database connections) that you want to run only once per test class. - Collection Fixtures: Use the
[Collection]attribute if you need to share a context across multiple test classes.
Points of Caution
- Instances are Disposable: xUnit creates a new instance for every test method. Changes made to
_stackinTest1do not affect_stackwhenTest2runs; it always starts from an empty state. - Need for Dispose: If you are only handling simple objects in memory (like
ListorStack), the Garbage Collector handles them, so you do not strictly need to implementDispose. Use it only for unmanaged resources like file handles or database connections.
Application
Asynchronous Initialization and Cleanup
If your setup requires asynchronous processing (async/await), implement the IAsyncLifetime interface.
public class AsyncTests : IAsyncLifetime
{
public async Task InitializeAsync()
{
// Async setup (e.g., populating a database)
await PrepareDatabaseAsync();
}
public async Task DisposeAsync()
{
// Async cleanup
await CleanDatabaseAsync();
}
// ... [Fact] methods ...
}
Summary
In xUnit.net, write setup logic in the constructor and teardown logic in the Dispose method. This design of rebuilding the environment for every method eliminates dependencies between tests (side effects) and ensures that every test produces the same result regardless of the execution order.
