Write your first test
The sample has journeys for every integration. Your own first test is smaller: one request, one shape, one run of its own.
- Add a test file to the sample and run it by itself.
- Give the test its own data and assert the response with a shape.
- Point the test at a client the run composed.
- Install and run (lesson 1).
- The repository cloned and open in an editor.
The scenario
A test is easiest to trust when it is one journey you can read top to bottom. The sample's tests are written that way, and a new one starts the same: select the application, take a client from the context, send one request, assert what comes back.
This lesson follows the path in the sample's README, so the file you write matches the archive the next lesson reads.
1. Add the file
Create MyFirstJourney.cs in samples/Northstar.ProtoTest/:
1namespace Northstar.ProtoTest;23using System.Net;4using global::ProtoTest.Core;5using global::ProtoTest.Http;6using global::ProtoTest.NUnit;7using global::ProtoTest.Rest;8using global::ProtoTest.SampleApp.Contracts;910[Application(NorthstarTargets.Api)]11[NorthstarMember]12public sealed class MyFirstJourney13{14[ProtoTest]15[SignedInAs]16public async Task CreatingAProjectReturnsIt()17{18var name = $"first-{Proto.Context.TestId}";19using var created = await Proto.Context.Rest()20.Body(new CreateProjectRequest(name))21.PostAsync("/api/v1/projects");2223created24.Should.HaveHttpStatus(HttpStatusCode.Created)25.Should.MatchShape(new { name, status = ProjectStatuses.Active });26}27}
- Name the package namespaces with global::
The file lives in Northstar.ProtoTest, so a plain using would resolve ProtoTest to the sample namespace. global:: names the package namespace without ambiguity.
- Select the application
Application picks the API the run composed for this test.
- Provision the tenant and sign in
NorthstarMember is a composite: an isolated tenant for this test, and an authenticator that carries the member token. The tenant is removed at teardown.
- Start the context
[ProtoTest] is the runner attribute that creates the execution context around the test body and completes it afterwards.
- Name the data after the test
TestId is unique per run, so the name cannot collide with another test.
- Take the client from the context
Rest() carries the address the run composed; the test does not know a port.
- Assert what the response is
The status check and the shape check both record what they read, and the shape check reports the JSON path and both values when it fails.
samples/Northstar.ProtoTest/README.md. The next lesson reads the trace this test writes.2. Run it alone
From the repository root:
dotnet test samples/Northstar.ProtoTest --filter "FullyQualifiedName~MyFirstJourney"
The filter runs one test. The run is green, and it writes the same three files as the full suite, under the sample's output folder:
bin/Debug/net8.0/TestResults/prototest-{runId}.prototrace
The next lesson walks l1-first-journey.prototrace, the sample's own version of this journey. It is not the same recording: the archive's test is ProjectsJourney, its name starts with atlas- instead of first-, and it asserts more of the response. Compare the shape, not the values: both traces hold the four layers, one REST create, the request and response artifacts and a shape check.
3. Keep the file, or remove it
Keep MyFirstJourney.cs if you plan to continue into Level 6, which reuses it for its attribute and milestone exercises. This page's step 1 recreates it in a minute if you removed it.
Delete it when you are done with the level:
rm MyFirstJourney.cs
Run that from the sample folder, or delete the file in your editor. The suite is a fixture, not a scratchpad, so the repository stays unchanged without it and git status is clean. The sample's own journeys do not change either way.
Checkpoint
The project name is built from Proto.Context.TestId. Run the test twice. What do the two runs share, and what differs?
TestId is unique for every test run, so the name is unique too. The test never reads a name another run left behind. That is the state answer from Level 0, made concrete in one line.What you learned
- A first test is four attributes, one client call and one assertion.
- The name comes from the test id, so two runs cannot collide.
- The run leaves a trace for the test you wrote, not only for the sample.