runStateMachineTestCases<STATE extends Object, ACTION extends Object, MOCKS extends Object> function
void
runStateMachineTestCases<STATE extends Object, ACTION extends Object, MOCKS extends Object>({
- required String name,
- required MOCKS createMocks(),
- required StateMachineFactory<
STATE, ACTION, MOCKS> createStateMachine, - required List<
STATE> states, - required List<
ACTION> actions, - required StateMachineTestCases<
STATE, ACTION, MOCKS> cases, - StateMachineTestHook? beforeEach,
- StateMachineTestHook? afterEach,
Registers exhaustive Given-When-Then transition tests for a StateMachine.
The runner creates a fresh real StateMachine for each state/action pair, dispatches the action, verifies the immediate state, waits for the expected state stream emissions, and finally verifies the settled state.
It also registers a coverage test that fails when any states x actions
pair is missing from cases, or when unexpected cases exist.
afterEach is invoked for every generated transition test even when setup,
execution, or earlier cleanup fails.
Implementation
void runStateMachineTestCases<STATE extends Object, ACTION extends Object,
MOCKS extends Object>({
required String name,
required MOCKS Function() createMocks,
required StateMachineFactory<STATE, ACTION, MOCKS> createStateMachine,
required List<STATE> states,
required List<ACTION> actions,
required StateMachineTestCases<STATE, ACTION, MOCKS> cases,
StateMachineTestHook? beforeEach,
StateMachineTestHook? afterEach,
}) {
group(name, () {
test('covers every state/action pair', () {
_expectAllTransitionsCovered(
states: states,
actions: actions,
cases: cases,
);
});
for (final stateCase in cases) {
group(_stateLabel(stateCase._initial), () {
for (final actionCase in stateCase._actions) {
final description = actionCase._describe(stateCase._initial);
test(description, () async {
fsm.StateMachine<STATE, ACTION>? stateMachine;
_StateStreamProbe<STATE>? stateStreamProbe;
try {
await beforeEach?.call();
final mocks = createMocks();
final arrange = actionCase._arrange;
if (arrange != null) {
await arrange(mocks);
}
stateMachine = createStateMachine(mocks, stateCase._initial);
stateStreamProbe = _StateStreamProbe<STATE>(
stateMachine.stateStream,
);
final context = _failureContext(
beforeState: stateCase._initial,
action: actionCase._dispatch,
description: description,
);
stateMachine.dispatch(actionCase._dispatch);
expect(
stateMachine.state,
actionCase._afterState(stateCase._initial),
reason: context,
);
await stateStreamProbe.expectEmittedStates(
actionCase._emittedStates(stateCase._initial),
context,
);
expect(
stateMachine.state,
actionCase._finallyState(stateCase._initial),
reason: context,
);
final verify = actionCase._verify;
if (verify != null) {
await verify(mocks);
}
} finally {
try {
await stateStreamProbe?.cancel();
} finally {
try {
stateMachine?.close();
} finally {
await afterEach?.call();
}
}
}
});
}
});
}
});
}