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
- .NET
- Java
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();
}
}
public class MyScenario extends Scenario {
public int apiPort;
@Override
public void setupEnvironment() throws XceptoScenarioResetException {
try {
apiPort = startMyService();
} catch (Exception e) {
throw new XceptoScenarioResetException(
"Failed to start service", e);
}
}
@Override
public void stopEnvironment() throws XceptoScenarioResetException {
stopMyService();
}
}
Lifecycle phases
- .NET
- Java
The .NET lifecycle has three phases:
| Phase | Method | Purpose |
|---|---|---|
| Setup | Setup(ScenarioSetupBuilder) | Register services, run async setup tasks |
| Initialize | Initialize(ScenarioInitializationBuilder) | Optional — runs after services exist |
| Cleanup | Cleanup(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.
The Java lifecycle has two phases:
| Phase | Method | Purpose |
|---|---|---|
| Setup | setupEnvironment() | Start processes, servers, or containers |
| Stop | stopEnvironment() | Teardown; always runs |
Configuration (ports, URIs, credentials) is exposed as public fields on the scenario object.
Passing configuration to tests
Expose ports, URIs, or credentials as public members so the test can read them when building adapters:
- .NET
- Java
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();
}
}
public class WarehouseScenario extends Scenario {
public URI apiAddress;
public URI guiAddress;
@Override
public void setupEnvironment() throws XceptoScenarioResetException {
int apiPort = startApi();
int guiPort = startGui();
apiAddress = URI.create("http://localhost:" + apiPort);
guiAddress = URI.create("http://localhost:" + guiPort);
}
}
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
builderlambda
Keep test-specific behavior in the test method. The scenario is shared infrastructure.