Skip to main content

Know when the host starts and stops

The problem​

Nothing in the sample's tests calls Setup.Configure, yet every test finds a database, an application and a trace sink ready. Something starts them before the first test and something stops them after the last.

You need to know who does that, and why a test can never add to the composition.

Do it​

1. Find the setup class​

The setup class configures the host for a test project. Open samples/Northstar.ProtoTest/Setup.cs. It is short at the top because its base class does the work:

Setup.cs2 notes
1[SetUpFixture]
2public sealed class Setup : ProtoTestAssembly
3{
4protected override void Configure(IProtoHostBuilder builder)
5{
6var configuration = LoadConfiguration();
7var run = NorthstarRun.From(configuration);
8run.PrepareOwnedStore();
9
10ConfigureInfrastructure(builder, run);
11ConfigureApplications(builder, run);
12ConfigureDomain(builder, run);
13// ... tracing, sinks, the run gate and messaging
14}
15}
  1. NUnit's once-per-assembly hook

    The base class carries the attribute and the setup and teardown methods. Deriving from ProtoTestAssembly is the whole registration.

  2. You only write Configure

    It receives the builder. A test body never sees the builder, so the composition stays one readable list.

From samples/Northstar.ProtoTest/Setup.cs, trimmed to the declaration and the three composition calls.

2. See what the base class does with it​

The base class starts the host once before any test and stops it once after the last. This is NUnit's:

ProtoTestAssembly.cs2 notes
1[SetUpFixture]
2public abstract class ProtoTestAssembly
3: ProtoTestAssemblyHost<ProtoTestAssembly>, IProtoTestAssemblyHost<ProtoTestAssembly>
4{
5[OneTimeSetUp]
6public Task GlobalSetUp() => StartAsync(Configure);
7
8[OneTimeTearDown]
9public Task GlobalTearDown() => StopAsync();
10
11protected abstract void Configure(IProtoHostBuilder builder);
12}
  1. Start once

    Before any test, the base builds the host from Configure and starts it.

  2. Stop once

    After the last test, the base stops the host and disposes it.

From src/ProtoTest.NUnit/ProtoTestAssembly.cs. The other runner packages hook into their own runner's start and end, then take the same path below.

Under that, every runner takes the same three steps:

ProtoTestHostLifetime.cs3 notes
1var builder = new ProtoHostBuilder();
2configure(builder);
3var host = builder.Build();
4await host.StartAsync().ConfigureAwait(false);
  1. Configure composes

    Your Configure adds applications, integrations, sinks, hooks and gates to one builder.

  2. Build is the last step

    It creates the host. After it, any registration throws: "The ProtoHostBuilder has already built a ProtoHost; configure a new builder instead."

  3. Start opens the run

    Run hooks run, capabilities are recorded and infrastructure starts. Only then do tests start.

From src/ProtoTest.Core/ProtoTestHostLifetime.cs, the shared start path behind every runner.

3. Look for the end of the run in a trace​

Open l1-first-journey.prototrace in the viewer. The host writes its own work in the run layer: the part of the trace that belongs to no single test. At the end, three entries release run resources after the test's teardown:

EntryWhat it releases
Release · messaging:brokerthe run's messaging adapter, in memory in this recording
Release · readiness:application:Northstar webthe readiness check for the web application
Release · application:loopback:Northstar webthe listener the browser journey follows

What happened​

The runner called your Configure once, built the host, started it, ran the tests, then stopped it. The test never owned the messaging adapter or the listener, so no test teardown could release them. The host did, once, at the end.

Build is the last step on purpose. A registration after it would change a run that already started, so the builder throws. Each test still gets its own context and clients. Those are per-test state, not registrations.

In l2-broker-skip.prototrace the only selected test skipped. The host still started, recorded its capabilities and released the same three resources.

Check yourself​

The first journey's trace lists seven capabilities and three run resources. Who releases the three, and when, relative to the test?

Verify
Open l1-first-journey.prototrace and read the run layer: the capabilities at its top, the releases at its end.

Remember​

  • One host per test project: built from Configure, started before the first test, stopped after the last.
  • A test cannot add to the host. Registrations after Build() throw.
  • The run layer of a trace records the host's own work: capabilities, resources and releases.

Go deeper​