In this lesson, you will learn how to optimize your test suite performance by implementing shared environmental states using class and assembly-level fixtures.
When writing integration tests, repeatedly initializing expensive resources—like launching a Docker container, spinning up a database connection, or configuring a heavy dependency injection container—for every individual test method leads to slow, brittle suites. To solve this, we move beyond method-level setup to higher-level lifecycle management.
Class Fixtures
In xUnit, IClassFixture<T> allows you to share an object instance across all tests within a single test class. The framework ensures the fixture object is instantiated once before the first test in the class runs and is disposed of after the last test completes.
public class DatabaseFixture : IDisposable {
public DatabaseFixture() { /* Initialize shared connection here */ }
public void Dispose() { /* Cleanup shared resources */ }
}
public class UserTests : IClassFixture<DatabaseFixture> {
private readonly DatabaseFixture _fixture;
public UserTests(DatabaseFixture fixture) => _fixture = fixture;
// Database connection is now reused across all methods in this class
}
Assembly-Level Fixtures
While class fixtures handle groupings, sometimes you need a "global" state that persists across the entire test project, such as a mock API server or an orchestrated infrastructure environment. Since xUnit doesn’t have a global [AssemblyInitialize] attribute, we use the ICollectionFixture<T> pattern coupled with a CollectionDefinition.
You create a dedicated class to hold the definition, which acts as a "glue" for tests across multiple files. By sharing the same CollectionDefinition name, all classes in that collection share the exact same fixture instance, which is initialized once at the start of the execution and torn down at the very end.
Strategic Considerations
While global fixtures significantly reduce execution time, they introduce state dependency. Always ensure your setup methods reset or "clean" the state of your global resources between tests. If your tests rely on global state that isn't isolated, you risk "flaky" tests where the outcome of one test changes the behavior of another. Use these fixtures for read-only or self-resetting infrastructure, keeping your specific test data isolated at the method level.
Key Takeaway: Use Class Fixtures for shared dependencies within a single scope and Collection Fixtures for project-wide infrastructure to balance high-performance execution with clean test isolation.