Skip to main content

Decide what belongs in the host

The problem​

Adding infrastructure to host setup is convenient. But unnecessary startup can slow a suite, and shared mutable data can make tests interfere. Choose the resource's lifetime before registering it.

Registration alone does not decide a lifetime. This lesson helps you choose an owner and a lifetime.

Do it​

1. Ask two questions​

For any piece, ask who shares it and how long it lives. The answers pick the placement:

Who shares itHow long it livesPlacementThe sample's pieceWho releases it
Several tests use one instanceUntil the run endsrun-owned resourcea broker container or loopback listenerthe host
Each test needs its own instanceFor one testtest-owned resourcea tenant or per-test WireMock serverthe test's teardown
One test needs a local disposableWithin one operationa local variablea response held with usingthe test code
One test uses a run-lived instanceUntil the run endsrun-owned resource, if that lifetime is usefula WireMock server registered with PerRun()the host
Tests use an externally owned systemManaged outside the suiteconfigured address and clienta published application or existing brokerthe external owner

Choose the lifetime the resource needs, then register cleanup with that owner. Several tests can use the same registration while each receives a separate instance.

For an external system, choose whether the test needs the real service or a fake. The host can configure a client without owning the remote service's lifetime.

2. Spot the three traps​

Leaving ownership and evidence implicit. A raw client or manually started container needs explicit cleanup and instrumentation. Test code can dispose it directly or register it as a test-owned resource.

The environment drill in the failure tour shows the tracing limit. It calls the API with a plain HttpClient, which ProtoTest does not record, so the trace has no http.request operation for it. The trace still records the test's duration and failure. Prefer the configured client when it provides the behavior you need.

Choosing a longer lifetime without a reason. WireMock uses a separate server per test by default. PerRun() shares a server, its stubs and its request log until the run ends. The server starts when a test resolves it, not merely when the host registers it.

A run-lived fake can be useful even if only one journey uses it. Choose that lifetime deliberately, and account for shared stubs and request history if other tests use it later.

Sharing mutable seed data. Tests can interfere when they update or delete the same seeded records. Prefer per-test data for mutable scenarios. Shared reference data can work when setup is explicit and tests leave it unchanged.

3. See the third trap in a trace​

Open l0-state-drill.prototrace and l0-state-fix.prototrace in the viewer. The drill assumes a project exists and receives 404. The fix creates a project in its test-owned tenant before reading it.

The drill shows an unmet data assumption. The fix shows explicit setup.

What happened​

Two answers decided where each piece went: who shares it, and how long it must live. A tenant used by one test belongs to that test, and its teardown removes it. A broker used by every test belongs to the host, which releases it after the last test. A response held for one call is a local variable you dispose yourself.

The trace sees only what goes through ProtoTest's clients. A raw HttpClient works, but it leaves no operation behind.

When several tests need the same setup behavior, a hook, attribute, client or integration can reuse it through the existing lifecycle.

Check yourself​

A run hook seeds five projects. Several tests update and delete those same projects. What can go wrong, and how would you isolate the tests?

Remember​

  • Choose lifetime, sharing and cleanup ownership explicitly.
  • Raw resources need cleanup and instrumentation even when a test owns them.
  • Isolate mutable test data. Define shared reference data deliberately.

Go deeper​

  • Recipes: common journeys with the composed client, from REST to GraphQL to a workbook.
  • Extending ProtoTest: when the right answer is your own hook, attribute or integration.
  • The foundation: the lifecycle those extensions plug into.