Skip to main content

Scenario

A Scenario owns the environment lifecycle: starting external processes, containers, or in-process test servers before the state machine runs, and stopping them afterwards.

Cleanup always runs — even if setup, initialization, or the test itself fails.

Basic structure

public class MyScenario : XceptoScenario
{
public int ApiPort { get; private set; }

protected override ScenarioSetup Setup(ScenarioSetupBuilder builder)
{
builder.Do(async provider =>
{
ApiPort = await StartMyServiceAsync();
});
return builder.Build();
}

protected override ScenarioCleanup Cleanup(ScenarioCleanupBuilder builder)
{
builder.Do(_ => StopMyService());
return builder.Build();
}
}

Lifecycle phases

The .NET lifecycle has three phases:

PhaseMethodPurpose
SetupSetup(ScenarioSetupBuilder)Register services, run async setup tasks
InitializeInitialize(ScenarioInitializationBuilder)Optional — runs after services exist
CleanupCleanup(ScenarioCleanupBuilder)Teardown; always runs

Use builder.AddServices(sc => ...) inside Setup to register services into the scenario's service provider. Use builder.RegisterDisposable<T>() to let Xcepto dispose registered services during cleanup.

Passing configuration to tests

Expose ports, URIs, or credentials as public members so the test can read them when building adapters:

public class WarehouseScenario : XceptoScenario
{
public Uri ApiAddress { get; private set; }
public Uri GuiAddress { get; private set; }

protected override ScenarioSetup Setup(ScenarioSetupBuilder builder)
{
builder.Do(async _ =>
{
var apiPort = await StartApiAsync();
var guiPort = await StartGuiAsync();
ApiAddress = new Uri($"http://localhost:{apiPort}");
GuiAddress = new Uri($"http://localhost:{guiPort}");
});
return builder.Build();
}
}

What belongs in a scenario

In a scenario:

  • Starting/stopping containers, processes, or in-process servers
  • Seeding databases with fixed test infrastructure data
  • Exposing addresses, ports, and credentials

Not in a scenario:

  • Test-case-specific data (entity IDs, usernames, expected values)
  • Assertions
  • Adapter configuration — that goes in the builder lambda

Keep test-specific behavior in the test method. The scenario is shared infrastructure.