Foundation overview
ProtoTest is an integration testing foundation for .NET. Its integrations build on the same concepts from ProtoTest.Core. Learn these once and every integration makes sense. One host per process, one context per test; everything else hangs off these two. New to integration testing? The Learn track starts from why these tests get hard.
The pieces
| Piece | Job | Page |
|---|---|---|
ProtoHost | built once per test process; owns DI, run hooks, gates, infrastructure, and each test's start and completion | Lifecycle |
ProtoExecutionContext | exists for exactly one test; holds its clients, state, resources, findings, attachments and observations | Execution context |
| Hooks | run around every test or the whole run; registered on the host | Hooks |
| Attributes | run around the tests they decorate; package a capability onto any test | Attributes |
| Clients | what integrations give you: Rest(), GraphQL(), Web() | Clients |
| Assertions | one Should / ShouldNot surface on every integration | Assertions |
| Attachments | files a test produces, handed to the runner and the trace | Attachments |
| Skip conditions | stop a test before its lifecycle starts when the host cannot run it | Skip conditions |
Two things build on top and have their own sections:
- ProtoTrace records every operation, automatically.
- Observations and coverage turn what tests did into reports.
Two pieces matter less often. The context can own test-scoped resources and record findings without failing the test. The host builder can gate the whole run on what the reports collected.
Test time and Concurrency cover two questions every suite meets: how to move a clock instead of sleeping, and what ProtoTest keeps isolated when tests run in parallel.
A test, end to end
[Application("Api")] // attribute: selects the system under test
[SampleEnvironment] // attribute: provisions a tenant, deletes it afterwards
[Auth<SampleUserAuthenticator>] // attribute metadata read by the HTTP hooks
public sealed class BillingTests
{
[ProtoTest] // runner attribute: starts and completes the context
[SampleUser(SampleRoles.BillingAdministrator)]
public async Task OpenInvoicesAreListed()
{
var user = Proto.Context.Resolve<SampleUserContext>(); // typed state an attribute set
using var response = await Proto.Context.Rest() // client, created for this test
.GetAsync("/api/billing/invoices", new { state = "open" });
response.Should.HaveHttpStatus(HttpStatusCode.OK); // traced, observed, attached
}
}
What happens around that method:
- [1] The runner calls
StartTestAsync. A context and DI scope are created. - [2] Test hooks run, including ProtoTest's own, which creates clients and applies
[Auth<T>]. - [3] Attributes run in
Order:[SampleEnvironment]at -200, then[SampleUser]at -100. - [4] Your test body runs. Requests, assertions and recorded state are traced. A failing
Resolveor client lookup is traced too. - [5] The runner calls
CompleteTestAsync. Attributes and hooks tear down in reverse, attachments are published, and owned resources and the scope are disposed.
The same five steps in a recording, the sample suite's project journey:
- 1-2 Setup500.5 ms
Steps 1 and 2 of the lifecycle: hooks create the clients, attributes provision the tenant.
Recorded operations (3)
- Client initializer, SQL, scenario, auth hooks
- Rest, GraphQL, loopback web, in-process, probe, messaging
- Application, NorthstarTenant, SignedInAs, NorthstarMember
- 3 Execution178.0 ms
Step 3: the body runs. One request and the two checks that decided the test.
Recorded operations (3)
- REST POST /api/v1/projects
- Assert status 201 Created
- Assert response shape
- 4-5 Teardown36.7 ms
Steps 4 and 5: attributes and hooks reverse, attachments publish, resources release.
Recorded operations (3)
- NorthstarMember down to Application
- Request, response, expected shape, scenario summary
- Tenant cleanup, services, connection, consumer
docs/static/lessons/l1-first-journey.prototrace. The viewer draws the same trace from that archive (download it and drop it on the viewer).Lifecycle covers the exact rules, including what happens when something fails.
Where to go next
- Vocabulary: every term above, and the trace entity each one records.
- Integrations map: every package that plugs into this model.
- Observability: what the run records and how to read it.
- Recipes: common journeys, built from these pieces.