GM.Testing.Messaging 1.0.0

dotnet add package GM.Testing.Messaging --version 1.0.0
                    
NuGet\Install-Package GM.Testing.Messaging -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GM.Testing.Messaging" Version="1.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GM.Testing.Messaging" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="GM.Testing.Messaging">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GM.Testing.Messaging --version 1.0.0
                    
#r "nuget: GM.Testing.Messaging, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GM.Testing.Messaging@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GM.Testing.Messaging&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=GM.Testing.Messaging&version=1.0.0
                    
Install as a Cake Tool

GM.Testing

Shared testing utilities for the GM.* ecosystem — WebApplicationFactory helpers, in-memory fakes of the GM infrastructure abstractions, published-event capture, and terse HTTP assertions, so tests for services built on GM.* packages don't need real infra.

Test-only. Every package here is marked DevelopmentDependency (see below), so it never flows as a transitive runtime dependency. Reference these from test projects only.

Package structure — one broad package vs. several thin ones

You asked me to flag this trade-off. GM.Testing consumes many GM.* packages, so a single package would force every test project — even one unit-testing a pure domain function — to drag in EF Core, Testcontainers (Docker), ASP.NET's test host, Wolverine, and every GM abstraction transitively. NuGet has no "optional dependency": a package's dependencies are always transitive. So bundling is the wrong default.

Decision: split by concern, mirroring how the ecosystem already splits (.Redis / .Http / .Mediator / .AspNetCore). Each package carries only the dependencies its concern needs:

Package Gives you Heavy deps it pulls Status
GM.Testing HTTP assertion helpers (status + body in one call) none (framework only) ✅ built
GM.Testing.AspNetCore GmWebApplicationFactory<TProgram> Microsoft.AspNetCore.Mvc.Testing ✅ built
GM.Testing.Fakes In-memory ICacheService / IDistributedLock / IFileStorageService GM.Caching / .DistributedLock / .FileStorage (all light) ✅ built
GM.Testing.Messaging Published-event capture for GM.Messaging WolverineFx ✅ built
GM.Testing.Mediator Invoke handlers directly / strip pipeline behaviors GM.Mediator ✅ built
GM.Testing.EntityFramework Ephemeral DB fixtures (Testcontainers + EF) Testcontainers, EF Core ✅ built

Plus the dependency-free test data builders (TestDataBuilder<T>) that ship in the core package.

The split that matters most is isolating GM.Testing.EntityFramework — Testcontainers pulls a Docker client and EF pulls a provider; a fast unit-test project should never inherit those just to use a cache fake. The core stays dependency-free so it's safe to reference anywhere.

The one judgement call: the three lightweight fakes share GM.Testing.Fakes rather than one package each (GM.Testing.Caching, …). They're tiny, single-dependency doubles, and a test project usually touches several, so bundling them keeps the package count sane. If you later want a caching-only test project to avoid pulling GM.Messaging/FileStorage, they can split — the types wouldn't change.

GM.Testing — HTTP assertions

var order = await client.GetAsync("/orders/1").Result.ShouldBeOkAsync<OrderDto>();  // 200 + body
await (await client.PostAsJsonAsync("/orders", req)).ShouldBeCreatedAsync();          // 201
await (await client.GetAsync("/orders/absent")).ShouldBeNotFoundAsync();              // 404

On a status mismatch the failure message includes the response body, so a red test tells you why the server rejected the request. No test-framework dependency — works under xUnit/NUnit/MSTest.

GM.Testing.AspNetCore — WebApplicationFactory

GmWebApplicationFactory<TProgram> is a reusable test-host base for GM.API-based services (or any minimal-API / MVC app) with a fluent surface for the three things every integration test needs:

using var factory = new GmWebApplicationFactory<Program>()
    .WithEnvironment("Testing")
    .WithConfig("ConnectionStrings:Db", "…")        // in-memory config overrides
    .WithServices(s => s.AddGMTestingFakes());       // test DI overrides (win over the app's registrations)
var client = factory.CreateClient();

ConfigureTestServices runs after the app's own registration, so .WithServices / .ReplaceService overrides always win — that's how you swap a real cache/lock/storage for a fake.

GM.Testing.Fakes — in-memory doubles

services.AddGMTestingFakes();      // all three, replacing real registrations
// or individually: AddFakeCache() / AddFakeDistributedLock() / AddFakeFileStorage()
  • FakeCacheService (ICacheService) — deterministic, single-flight GetOrCreateAsync, honours absolute/sliding TTL against an injectable TimeProvider (fast-forward a test clock to prove expiry). Inspect Count / Keys / TryPeek.
  • FakeDistributedLock (IDistributedLock) — real in-process mutual exclusion so lock-guarded code can be tested under contention, or AlwaysAcquire = true for a no-op lock. Inspect HeldResources.
  • FakeFileStorageService (IFileStorageService) — byte-array backed; honours ExpectedChecksum (SHA-256 → ChecksumMismatchException) and not-found semantics. Inspect Keys / GetBytes.

GM.Testing.Messaging — published-event capture

services.AddCapturingMessageBus();   // replaces Wolverine's IMessageBus
// …exercise the handler, then:
var bus = provider.GetRequiredService<CapturingMessageBus>();
var evt = bus.ShouldHavePublished<OrderPlacedEvent>();   // asserts exactly one, returns it
Assert.Equal(orderId, evt.OrderId);

⚠️ Flag — hand-rolled Wolverine fake

GM.Messaging publishes through Wolverine's IMessageBus, a large (18-member), fast-moving third-party interface. CapturingMessageBus implements it by hand — capturing publish/send/invoke and no-op'ing the routing/streaming surface. That's a maintenance cost: a major Wolverine upgrade can change the interface and require an update here (the package pins WolverineFx 6.24.2). The alternative is Wolverine's own in-memory testing (IHost.TrackActivity() / a stub transport), which is upgrade-proof but couples your test to a running Wolverine host. This package favours the terse, broker-free, host-free capture; reach for Wolverine's tracking when you need full routing fidelity.

<a name="test-only"></a>Keeping it test-only

Every package sets <DevelopmentDependency>true</DevelopmentDependency>, which stamps developmentDependency="true" into the nuspec. NuGet then gives it PrivateAssets="all" semantics in consumers, so if a project references GM.Testing it won't flow to that project's downstream consumers — a production package can't accidentally ship a testing dependency. The discipline still holds: reference GM.Testing.* from test projects only.

GM.Testing.Mediator — handler test helpers

// Bypass the whole pipeline — exercise just the handler:
var result = await provider.InvokeHandlerAsync<PlaceOrder, OrderResult>(new PlaceOrder(...));

// Or run through the mediator with selected behaviors stripped:
services.AddGMMediator(assembly);
services.RemovePipelineBehaviors();                              // all of them, or…
services.RemovePipelineBehavior(typeof(IdempotencyBehavior<,>)); // …just this one

So unit tests skip idempotency/validation/logging while integration tests keep the full pipeline.

GM.Testing.EntityFramework — ephemeral DB fixtures

Generic over any DbContext (including a GM.EntityFramework.Persistence GenericDbContext-derived one).

// Real Postgres via Testcontainers (needs Docker) — applies migrations on start, tears down on dispose:
await using var db = new PostgresDatabaseFixture<AppDbContext>(opts => new AppDbContext(opts));
await db.InitializeAsync();
await using var ctx = db.CreateContext();

// Docker-free fallback for fast unit tests (EF in-memory provider):
await using var db = new InMemoryDatabaseFixture<AppDbContext>(opts => new AppDbContext(opts));

The contextFactory delegate means any constructor shape works. It pairs with GM.EntityFramework.Persistence conventions without taking a versioned dependency on that package — same decoupling philosophy as the rest of the ecosystem.

Test data builders (in core)

TestDataBuilder<T> is a dependency-free fluent base for object-mothers — works for mutable classes (.With(x => …)) and immutable records (.Customize(x => x with { … })), with .Build(), .BuildMany(n), and an implicit conversion to T.

Runnable usage lives in GM.Testing.Samples.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 79 8/7/2026