Skip to main content

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​

PieceJobPage
ProtoHostbuilt once per test process; owns DI, run hooks, gates, infrastructure, and each test's start and completionLifecycle
ProtoExecutionContextexists for exactly one test; holds its clients, state, resources, findings, attachments and observationsExecution context
Hooksrun around every test or the whole run; registered on the hostHooks
Attributesrun around the tests they decorate; package a capability onto any testAttributes
Clientswhat integrations give you: Rest(), GraphQL(), Web()Clients
Assertionsone Should / ShouldNot surface on every integrationAssertions
Attachmentsfiles a test produces, handed to the runner and the traceAttachments
Skip conditionsstop a test before its lifecycle starts when the host cannot run itSkip conditions

Two things build on top and have their own sections:

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. [1] The runner calls StartTestAsync. A context and DI scope are created.
  2. [2] Test hooks run, including ProtoTest's own, which creates clients and applies [Auth<T>].
  3. [3] Attributes run in Order: [SampleEnvironment] at -200, then [SampleUser] at -100.
  4. [4] Your test body runs. Requests, assertions and recorded state are traced. A failing Resolve or client lookup is traced too.
  5. [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:

The lifecycle, recordedNorthstar.ProtoTest.ProjectsJourney.CreatingAProjectReturnsIt
  1. 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 hookshook.before · 6 hooks, lowest order first
    • Rest, GraphQL, loopback web, in-process, probe, messagingclient.initialize · one operation per client
    • Application, NorthstarTenant, SignedInAs, NorthstarMemberattribute.before · 4 attributes in Order
  2. 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/projectshttp.request · 158.5 ms, 201 Created
    • Assert status 201 Createdassert.http.status · the check that decided the request
    • Assert response shapeassert.json.shape · 5 properties matched at once
  3. 4-5 Teardown36.7 ms

    Steps 4 and 5: attributes and hooks reverse, attachments publish, resources release.

    Recorded operations (3)
    • NorthstarMember down to Applicationattribute.after · reverse of setup
    • Request, response, expected shape, scenario summaryattachment.publish · 4 files into the archive
    • Tenant cleanup, services, connection, consumerresource.release · reverse registration order
Read from the project journey the first lesson writes in 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.