implementation reference
Integration Test Harness
Provides guidance for building integration tests with Basis.Testing, Docker containers, and gRPC clients in src/services/*/*.Tests, ensuring consistent environment provisioning and assertions.
Integration Test Harness Composer
Use this skill to author robust integration tests that exercise accessors, managers, and engines with real infrastructure dependencies.
When to Use
- Creating new test classes under
*.Tests - Adding coverage for new accessor operations
- Validating MassTransit or storage integrations using Docker containers
- Troubleshooting flaky integration tests
Test Host Pattern
- Initialize TestHost – Start with
new TestHost()from Basis.Testing and chain configuration methods. Example: CompanyProfileAccessTests. - Attach Output – Call
.WithTestOutput(_testOutputHelper)to capture logs in xUnit. - Provision Containers – Use
.WithDockerContainer<T>()for required dependencies:- PostgreSQL via PostgresContainer
- RabbitMQ via MassTransitRabbitMqContainer
- Azurite via AzuriteContainer
- Configure Services – Within
.ConfigureServices, register gRPC options, Marten stores, MassTransit bus, and any fakes. - Register Service Under Test – Use
.WithService<IInterface, Implementation>()or custom factories to expose MagicOnion services. - Start Host – End the chain with
.StartAsync()and retain the returned TestHost reference for cleanup.
Marten Setup Guidelines
- Pull connection string from the provisioned container:
var postgres = containers.Get<PostgresContainer>();. - Configure
AddMartenwithoptions.Connection(postgres.ConnectionString). - Use
AutoCreate.Allonly in tests to simplify schema management.
RabbitMQ & MassTransit
- Use
MassTransitRabbitMqContainerto emulate broker behavior. - Configure MassTransit inside
ConfigureServices, pointing tocontainer.ConnectionStringand default credentials. - Include bus start/stop within test lifecycle if needed.
Azurite Usage
- Include when testing blob, queue, or table storage interactions.
- Retrieve connection strings from the container helper and register storage clients accordingly.
Lifecycle Management
- Implement
IAsyncLifetimeon test class; start host in the test method and dispose inDisposeAsync. - Always await
_testHost.DisposeAsync()to stop containers cleanly. - Handle cancellations by passing tokens if tests allow early termination.
Assertions & Helpers
- Create gRPC clients via
_testHost.CreateMagicOnionClient<TService>(). - For HTTP hosts, use
_testHost.CreateHttpClient()when available. - Use
WithHttpShim<TService>()to auto-generate HTTP endpoints for testing. - Prefer Shouldly or xUnit assertions to maintain consistency.
Note: The correct method is CreateMagicOnionClient<TService>(), not CreateGrpcClient<T>().
Checklist Before Shipping Tests
- Containers provisioned for all external dependencies
- Services configured with container connection strings
- Tests verify both success and failure scenarios
- Resources disposed in
DisposeAsync - Logging enabled for easier debugging
- Test data seeded using domain-relevant values (no lorem ipsum IDs)
- Assertions verify spec-compliant responses including unions and validation edge cases
Troubleshooting Tips
- If container health checks fail, increase timeout or inspect connection strings.
- For Marten failures, ensure database name/credentials match container configuration.
- When gRPC calls throw unhandled exceptions, enable
EnableDetailedErrorsduring test configuration. - Capture logs by inspecting
_testHost.TestOutputor container logs for additional diagnostics. - For flaky tests, ensure containers are not reused across tests unless explicitly supported.
Task Playbooks
Write a MagicOnion Accessor Integration Test
- Implement
IAsyncLifetimeto control setup/teardown. - Build a
TestHostwith output and required containers (usually Postgres). - Register gRPC detailed errors and Marten configuration using container connection string.
- Call
.WithService<IInterface, Implementation>()for the accessor. - Start the host and create a gRPC client via
_testHost.CreateMagicOnionClient<IInterface>(). - Execute store/fetch operations with realistic payloads.
- Assert responses and persisted state using Marten sessions or subsequent fetches.
- Dispose host in
DisposeAsync.
Add MassTransit Coverage
- Add
.WithDockerContainer<MassTransitRabbitMqContainer>(). - In
ConfigureServices, set up MassTransit with the container’s connection string and register consumers. - Start TestHost and optionally capture bus handle for advanced scenarios.
- Publish or send messages through MassTransit clients and assert outcomes (database changes, responses).
- Clean up resources and ensure queues are ephemeral or explicitly deleted.
Acceptance Criteria
- Tests stand up required infrastructure automatically without manual prerequisites.
- Service endpoints are exercised end-to-end through gRPC or HTTP clients, not merely unit mocks.
- Assertions validate both data persistence and contract conformance.
- Test teardown disposes hosts and containers reliably, preventing resource leaks between runs.
- Logging and diagnostics are enabled to aid future debugging.
Follow this workflow to keep integration tests deterministic, maintainable, and aligned with the Lista testing infrastructure.