with one click
testcontainers-shared-fixture
// MyBlog integration-test convention for sharing one MongoDbFixture per xUnit domain collection in tests/Integration.Tests while keeping per-test database isolation.
// MyBlog integration-test convention for sharing one MongoDbFixture per xUnit domain collection in tests/Integration.Tests while keeping per-test database isolation.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | testcontainers-shared-fixture |
| confidence | high |
| description | MyBlog integration-test convention for sharing one MongoDbFixture per xUnit domain collection in tests/Integration.Tests while keeping per-test database isolation. |
tests/Integration.Tests/Infrastructure/MongoDbFixture.cs ✅ Already implements
IAsyncLifetime for container lifecycle management.tests/Integration.Tests/Infrastructure/BlogPostIntegrationCollection.cs ✅
Defines the xUnit collection definition.tests/Integration.Tests/BlogPosts/MongoDbBlogPostRepositoryTests.cs ✅ Demonstrates
live MongoDB-backed integration suite (9 tests, all passing).One shared Mongo fixture per xUnit domain collection
Define collection definitions once:
[CollectionDefinition("BlogPostIntegration")]
public sealed class BlogPostIntegrationCollection
: ICollectionFixture<MongoDbFixture> { }
Apply the collection attribute to each test class:
[Collection("BlogPostIntegration")]
public sealed class MongoDbBlogPostRepositoryTests(MongoDbFixture fixture) { }
Collection names are domain-scoped: BlogPostIntegration, AuthorIntegration,
CommentIntegration (for future).
Do NOT reuse generic collection names like "MongoDb" or "Integration".
Per-test database isolation using unique database names
Every test must write to its own isolated database.
Generate unique names with: $"T{Guid.NewGuid():N}" (produces T prefix + 32-char GUID).
Pattern in CreateRepo() helper:
private MongoDbBlogPostRepository CreateRepo(string? dbName = null) =>
new(fixture.CreateFactory(dbName ?? $"T{Guid.NewGuid():N}"));
xUnit creates a fresh test-class instance per test method, so constructor or method-level database creation ensures isolation.
Fixture responsibility isolation
MongoDbFixture ONLY:
InitializeAsync().ConnectionString (read-only property).DisposeAsync().CreateFactory(dbName) to yield IDbContextFactory<BlogDbContext>.MongoDbFixture does NOT:
xUnit configuration for collection-level parallelism
File: tests/Integration.Tests/xunit.runner.json
Current configuration (✅ correct):
{
"parallelizeAssembly": false,
"parallelizeTestCollections": true
}
Rationale: No tests run in parallel within a single collection (one fixture per collection). Different collections CAN run in parallel (different containers).
Future scale: If 3–5 domain collections exist, parallelization will become a visible performance win.
Collection Definition (tests/Integration.Tests/Infrastructure/BlogPostIntegrationCollection.cs):
[CollectionDefinition("BlogPostIntegration")]
public sealed class BlogPostIntegrationCollection
: ICollectionFixture<MongoDbFixture> { }
Test Class (tests/Integration.Tests/BlogPosts/MongoDbBlogPostRepositoryTests.cs):
[Collection("BlogPostIntegration")]
public sealed class MongoDbBlogPostRepositoryTests(MongoDbFixture fixture)
{
private MongoDbBlogPostRepository CreateRepo(string? dbName = null) =>
new(fixture.CreateFactory(dbName ?? $"T{Guid.NewGuid():N}"));
[Fact]
public async Task AddAsync_persists_post_to_MongoDB()
{
// Arrange
var repo = CreateRepo();
var post = BlogPost.Create("Hello World", "Some content", "Author A");
// Act
await repo.AddAsync(post);
// Assert
var all = await repo.GetAllAsync();
all.Should().HaveCount(1);
all[0].Title.Should().Be("Hello World");
}
}
Fixture Implementation (tests/Integration.Tests/Infrastructure/MongoDbFixture.cs):
public sealed class MongoDbFixture : IAsyncLifetime
{
private readonly MongoDbContainer _container = new MongoDbBuilder().Build();
public string ConnectionString { get; private set; } = string.Empty;
public async Task InitializeAsync()
{
await _container.StartAsync();
ConnectionString = _container.GetConnectionString();
}
public async Task DisposeAsync() => await _container.DisposeAsync();
public IDbContextFactory<BlogDbContext> CreateFactory(string dbName) =>
new TestContextFactory(ConnectionString, dbName);
private sealed class TestContextFactory(string connectionString, string dbName)
: IDbContextFactory<BlogDbContext>
{
public BlogDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<BlogDbContext>()
.UseMongoDB(connectionString, dbName)
.Options;
return new BlogDbContext(options);
}
public Task<BlogDbContext> CreateDbContextAsync(CancellationToken ct = default) =>
Task.FromResult(CreateDbContext());
}
}
Create a new collection definition file:
[CollectionDefinition("{Entity}Integration")]
public sealed class {Entity}IntegrationCollection
: ICollectionFixture<MongoDbFixture> { }
Example: AuthorIntegrationCollection for Author domain tests.
Create a new test class directory:
tests/Integration.Tests/{Entity}/Mongo{Entity}RepositoryTests.cs
Apply the collection attribute and follow the same fixture pattern.
Each xUnit collection gets its own MongoDbFixture instance. xUnit
handles collection-level isolation automatically, so different collections
can run in parallel with separate fixtures and containers.
Verify xunit.runner.json still has parallelizeTestCollections: true.
CategoryIntegration,
IssueIntegration, CommentIntegration, StatusIntegration) that don't exist
in MyBlog yet. Create them only when the corresponding repository/handler tests
are written.IAsyncLifetime on integration test classes. Only
MongoDbFixture implements it. Test classes do not.