32. Testing Support

We have also added a set of utility classes to easy testing of a state machine instances. These are used in a framework itself but are also very useful for end users.

StateMachineTestPlanBuilder is used to build a StateMachineTestPlan which then have one method test() which runs a plan. StateMachineTestPlanBuilder contains a fluent builder api to add steps into a plan and during these steps you can send events and check various conditions like state changes, transitions and extended state variables.

Let’s take a simple StateMachine build using below example:

private StateMachine<String, String> buildMachine() throws Exception {
    StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();

    builder.configureConfiguration()
        .withConfiguration()
            .taskExecutor(new SyncTaskExecutor())
            .autoStartup(true);

    builder.configureStates()
            .withStates()
                .initial("SI")
                .state("S1");

    builder.configureTransitions()
            .withExternal()
                .source("SI").target("S1")
                .event("E1")
                .action(c -> {
                    c.getExtendedState().getVariables().put("key1", "value1");
                });

    return builder.build();
}

In below test plan we have two steps, first we check that initial state S1 is indeed set, secondly we send an event E1 and expect one state change to happen and machine to end up into a state S1.

StateMachine<String, String> machine = buildMachine();
StateMachineTestPlan<String, String> plan =
        StateMachineTestPlanBuilder.<String, String>builder()
            .defaultAwaitTime(2)
            .stateMachine(machine)
            .step()
                .expectStates("SI")
                .and()
            .step()
                .sendEvent("E1")
                .expectStateChanged(1)
                .expectStates("S1")
                .expectVariable("key1")
                .expectVariable("key1", "value1")
                .expectVariableWith(hasKey("key1"))
                .expectVariableWith(hasValue("value1"))
                .expectVariableWith(hasEntry("key1", "value1"))
                .expectVariableWith(not(hasKey("key2")))
                .and()
            .build();
plan.test();

These utilities are also used within a framework to test distributed state machine features and multiple machines can be added to a plan. If multiple machines are added then it is also possible to choose if event is sent to particular, random or all machines.

Above testing example uses hamcrest imports:

import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.collection.IsMapContaining.hasKey;
import static org.hamcrest.collection.IsMapContaining.hasValue;
import static org.hamcrest.collection.IsMapContaining.hasEntry;
[Tip]Tip

All possible options for expected are documented in javadocs StateMachineTestPlanStepBuilder.