Skip to main content

What a test leaves behind

A test that reads state it did not create depends on every run before it. That is the state question from lesson 1. It turns a green suite red without a code change.

Level 0, lesson 3About 8 minutes
By the end
  • Say what a passing test owns and what it removes.
  • Find the cleanup a run recorded in its trace.
  • Explain why leftovers make failures depend on run order.
Before you start
  • A failure tour (lesson 2).
  • Nothing installed. The archives are on this site.

The scenario​

The state drill asks for the project id prj_1 and gets a 404. The request is valid, the id looks valid, and no test in that run created the project. The drill did not trip over a bug; it read state that belonged to no one.

The paired fix, EachTenantSeesOnlyItsOwnProjects, creates a project first and then lists what its own tenant can see. It finds exactly one project: the one it created.

The pair, side by side​

The drill reads and the request 404s. The fix creates and then reads:

The drillThe fix
Before the readnothingCreate · CreateProjectRequest, provisioned in the test tenant
The callGET /api/v1/projects/prj_1, HTTP 404GET /api/v1/projects, HTTP 200
The checkexpected 200, got 404the list holds one project, the one this test created

The fix does not look up a well known id or seed a fixture in advance. It creates its own project, reads it, and lets teardown remove the tenant that holds it.

A passing run, end to end​

Read the fix's trace in three parts:

  • Setup opens the connection (sql.connection.open), provisions the tenant (data.create · ProvisionTenantRequest, then data.provision), and signs the member in. The tenant belongs to this test and no other.
  • Execution creates the project through the data client (data.create · CreateProjectRequest), reaches the application (project.create, reported by the application itself), and reads the list back over REST, with the status check on the response.
  • Teardown publishes the attachments, removes the tenant (data.cleanup · TenantResponse), and releases the resources the context owned, each with its own release entry.

A resource that is never released would show up as a missing release entry in that last part. That is why the teardown layer is worth reading even when a test passes.

Names that keep tests apart​

The sample runs its tests in parallel, eight at a time, and no identifier it creates can collide across tests:

  • The tenant name comes from context.UniqueName("northstar"), so each test owns its own tenant.
  • Project names are fixed inside that tenant, like atlas, or carry Proto.Context.TestId; either way no other test can reach them.
  • The teardown removes the tenant by the identity the setup recorded, not by a search.

That is what makes the state answer hold under a parallel run: the test reads only what it created, and what it created lives in a tenant no other test can reach.

What a leak looks like​

If the cleanup were missing, the trace would still hold the setup's data.create entry, and the teardown layer would not hold data.cleanup or the resource.release line for the tenant. The next run would start with that tenant still in the store. Reads would then depend on the order tests happen to run in, which is exactly the kind of failure that looks random.

Checkpoint​

The state fix provisions a tenant in setup and removes it in teardown. If the removal were missing, what would the trace show, and what would the next run see?

Verify
Download l0-state-fix.prototrace, open it in the viewer, and read its teardown layer next to its setup layer. The setup creates the tenant; the teardown removes it.

What you learned​

  • A test owns the state it creates and removes it on the way out.
  • The teardown layer of the trace is where you check that it did.
  • Names built from the test id are what keep parallel tests from colliding.

Keep exploring​