Write your first test
The problem
You have run the sample suite. Now add a test that creates a project and checks the response: one request, two checks, one filtered run.
The sample already configures the application and its clients, so you write only the test.
Do it
1. Add the file
Create MyFirstJourney.cs in samples/Northstar.ProtoTest/ with the code below. The callouts explain the setup, request and checks.
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}
- Use the package namespace
global:: starts namespace lookup at the root. This avoids confusing the ProtoTest packages with the enclosing Northstar.ProtoTest namespace.
- Select the application
Application selects the API configured in the sample setup class.
- Prepare a tenant and authentication
NorthstarMember groups tenant creation with an authenticator that sends the member token. The tenant provisioner also registers cleanup for teardown.
- Run through the NUnit adapter
This NUnit attribute marks the method as a test and wraps its lifecycle. The adapter creates and completes the test context, including setup and teardown.
- Declare the test identity
SignedInAs declares who the test acts as. With no role specified, the sample authenticator uses the tenant owner token.
- Include the test id in the name
The default generator gives each test in this host a different id. That does not guarantee unique names across separate runs.
- Retrieve the REST client
Rest() returns the client from this test context. The setup class supplies its address, so this test names no port.
- Check the HTTP status
HaveHttpStatus checks for 201 Created and records the comparison in the trace.
- Check the response fields
MatchShape checks name and status in the JSON body. A differing value produces a message with the JSON path, expected value and actual value.
2. Run it alone
From the repository root:
dotnet test samples/Northstar.ProtoTest --filter "FullyQualifiedName~MyFirstJourney"
The filter selects your new test. You should see one passed test and no failures. The run also writes a trace, the archive of recorded work from the run.
Find it under samples/Northstar.ProtoTest/, in the same folder as in lesson 1:
bin/Debug/net8.0/TestResults/prototest-{runId}.prototrace
3. Run it again
Run the same command a second time. You should see one passed test again and a new trace file.
The project name contains Proto.Context.TestId, which usually differs between runs but is not guaranteed to.
The sample isolates project data in a tenant created for each test. Its tenant provisioner registers cleanup that removes the tenant and its projects at teardown. Repeatability depends on that isolation and cleanup, not on the project name changing.
What happened
The example uses four parts of the configured sample:
- Context. The NUnit adapter creates a test context, the object behind
Proto.Context. It holds this test's clients, state and attachments. - Client.
Proto.Context.Rest()retrieves the REST client for the selected application. The client knows the API address, so the test supplies a path. - Data.
NorthstarMemberprepares a tenant and registers its cleanup.[SignedInAs]declares the identity that the sample authenticator uses for the request. - Checks. The status check expects HTTP 201 Created. The shape check compares
nameandstatus, allowing other response fields. Both checks record their comparisons.
You wrote the request and its expected result. The existing setup class and attributes supply the application, client, identity and data cleanup.
Check yourself
Why can this test run again without depending on the project name changing?
NorthstarMember prepares a tenant for each test and registers cleanup through its provisioner. The application keeps projects within that tenant, and teardown deletes its data.
The test id helps distinguish names within a run. Its random prefix does not guarantee uniqueness between runs, so it does not replace isolation or cleanup.
Remember
- This test sends one request and checks its status and two response fields.
- The client takes its address from the run, so the test names no port.
- The sample's tenant setup and cleanup keep test data separate. A generated name alone does not.
Go deeper
- Your first test: the same path against an application of your own.
- Keep
MyFirstJourney.csfor the next lesson, Read the trace. Write your own attribute also reuses it. - Next: Read the trace breaks this test on purpose and finds out why.