Pragmatic.Testing
Compile-time testing infrastructure for Pragmatic APIs: generated contract tests, a typed
test client (Api.*), and HTTP assertion helpers. Zero reflection: the source generator
reads the endpoint contracts your app already emits.
What you get
Section titled “What you get”| Piece | Where it lives | Purpose |
|---|---|---|
| Contract tests | generated into the test project | Authorization, CRUD and state-transition suites per endpoint; see Contract tests |
PragmaticTestIdentity |
Pragmatic.Testing runtime |
Dev-identity headers (AsUser) for acting as a specific user, tenant, or a caller with no permission at all |
Typed client Api.* |
generated into the test project | Api.{Boundary}.{Name}Async(client, ...) → ApiResponse / ApiResponse<T>; routes and verbs resolved at compile time |
ApiResponse / ApiResponse<T> |
Pragmatic.Testing runtime |
Lazy deserialization (ReadAsync), chainable assertions, implicit conversion to HttpResponseMessage |
PragmaticHttpAssertions |
Pragmatic.Testing runtime |
ShouldBeOk(), ShouldBeCreated(), ShouldBeUnprocessable(), ShouldBeConflict(), ShouldHaveStatus(code) … framework-agnostic (BCL-only) |
PragmaticJson.Options |
Pragmatic.Testing runtime |
The host’s JSON conventions, for hand-rolled serialization in tests |
.Should() assertions |
Pragmatic.Testing runtime (Assertions/) |
The assertion library the repository’s own tests use; no FluentAssertions |
| Mocks | Pragmatic.Testing.Mocking.SourceGenerator |
[GenerateMock<T>] → a reflection-free, AOT-safe mock; opt in by referencing the generator |
| Comparers | Pragmatic.Testing.Comparers.SourceGenerator |
[GenerateComparer<T>] → a member-by-member BeEquivalentTo for types without value equality |
The client pairs with the ApiRoutes class the unified source generator emits into the app
assembly (route constants + typed URL builders): rename a route and every test that calls
it breaks at build, not at runtime.
Quick Start
Section titled “Quick Start”The test project references the app plus both Testing pieces:
<ProjectReference Include="..\..\src\MyApp\MyApp.csproj" /><PackageReference Include="Pragmatic.Testing" Version="1.0.0-alpha.1" /><PackageReference Include="Pragmatic.Testing.SourceGenerator" Version="1.0.0-alpha.1" PrivateAssets="all" />(Inside this repository the same two are ProjectReferences, the generator with
OutputItemType="Analyzer" ReferenceOutputAssembly="false".)
using Pragmatic.Testing; // ApiResponse, assertions, PragmaticJsonusing Pragmatic.Tests.Generated; // generated Api client
var created = await Api.Booking.CreateGuestAsync(Client, new{ firstName = "Ada", lastName = "Lovelace", email = "ada@example.com"});created.Raw.ShouldBeCreated();
var fetched = await Api.Guests.GetGuestAsync(Client, guestId);var dto = await fetched.ReadAsync(); // ApiResponse<GuestDto> → GuestDtoGenerated contract test classes join the PragmaticContractTests xUnit collection;
PragmaticContractTestBase exposes the shared Client wired once by the collection
fixture, no per-class setup.
Status
Section titled “Status”Functional within 1.0.0-alpha: generated contract tests, the typed test client (Api.*), and the
HTTP assertions; four of the reference applications’ suites run on it. See the
roadmap.
| Getting started | Wiring the generator, the collection fixture, your first test |
| Contract tests | What each generated family asserts, and what it deliberately does not |
| Typed test client | The two tiers (ApiRoutes + Api), setup, usage, limits |
| Common mistakes | Denial tests that pass for the wrong reason, assertion strength, fixture wiring |
| Troubleshooting | Nothing generated, missing client, unexpected 401/400 |
Requirements
Section titled “Requirements”- .NET 10.0+
- xUnit (the generated contract tests are xUnit classes)
License
Section titled “License”Part of the Pragmatic.Design ecosystem. See Licensing. Pragmatic.Testing is MIT-licensed.
