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

  1. Initialize TestHost – Start with new TestHost() from Basis.Testing and chain configuration methods. Example: CompanyProfileAccessTests.
  2. Attach Output – Call .WithTestOutput(_testOutputHelper) to capture logs in xUnit.
  3. Provision Containers – Use .WithDockerContainer<T>() for required dependencies:
  4. Configure Services – Within .ConfigureServices, register gRPC options, Marten stores, MassTransit bus, and any fakes.
  5. Register Service Under Test – Use .WithService<IInterface, Implementation>() or custom factories to expose MagicOnion services.
  6. 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 AddMarten with options.Connection(postgres.ConnectionString).
  • Use AutoCreate.All only in tests to simplify schema management.

RabbitMQ & MassTransit

  • Use MassTransitRabbitMqContainer to emulate broker behavior.
  • Configure MassTransit inside ConfigureServices, pointing to container.ConnectionString and 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 IAsyncLifetime on test class; start host in the test method and dispose in DisposeAsync.
  • 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 EnableDetailedErrors during test configuration.
  • Capture logs by inspecting _testHost.TestOutput or 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

  1. Implement IAsyncLifetime to control setup/teardown.
  2. Build a TestHost with output and required containers (usually Postgres).
  3. Register gRPC detailed errors and Marten configuration using container connection string.
  4. Call .WithService<IInterface, Implementation>() for the accessor.
  5. Start the host and create a gRPC client via _testHost.CreateMagicOnionClient<IInterface>().
  6. Execute store/fetch operations with realistic payloads.
  7. Assert responses and persisted state using Marten sessions or subsequent fetches.
  8. Dispose host in DisposeAsync.

Add MassTransit Coverage

  1. Add .WithDockerContainer<MassTransitRabbitMqContainer>().
  2. In ConfigureServices, set up MassTransit with the container’s connection string and register consumers.
  3. Start TestHost and optionally capture bus handle for advanced scenarios.
  4. Publish or send messages through MassTransit clients and assert outcomes (database changes, responses).
  5. 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.