Working through this project gave me some new insights and made me realize that there is much sense in demonstrating things to students.

The exercise is about:

  • implementing a state machine using the GoF State Pattern,doc/bindings2.tex

    • ejectCoin(…​),

    • refill(…​)

    • and draw(…​) which can be understood as events, modeled in the interface State and should therefor have an implementation in each state.

  • and four states:

    • SoldOut,

    • NoCoin,

    • HasCoin

    • and Winner.

  • The initial project has some issues, design wise, but we will address them later.

Study the [state_diagram].

State machine

Study it, this is the (whole) specification of the first SUT.

We will start testing the state class. As we have chosen to implement the states using an enum, called StateEnum in this design, implementing the State interface.

The relation between the context and its states, designed according to the GoF design pattern State .

Context

Because both the Java artifacts Context and State in the initial design are interfaces I have added the UML public visibility attribute to all methods, because that is how interfaces work in Java.

A traditional way to present state and state machine is a state transition table. This technique predates UML, but is still valuable, in particular because it will list all possibilities and will therefor help to find more test cases, helping to ensure that the set of tests is complete.

In the table below, [empty] and [winner] are guard expressions, with outcome specified for the moment of evaluation. Where there is no value in the guard columns, the guard is not relevant. For the column End State, empty means no state change.

Start state End State Trigger [empty] [winner] Action

NoCoin

HasCoin

insertCoin(…​)

HasCoin

NoCoin

ejectCoin(…​)

HasCoin

NoCoin

draw(…​)

false

dispense(…​)

HasCoin

SoldOut

draw(…​)

true

dispense(…​)

HasCoin

Winner

draw(…​)

false

true

dispense(…​)

Winner

NoCoin

draw(…​)

false

dispense(…​)

Winner

SoldOut

draw(…​)

true

dispense(…​)

SoldOut

NoCoin

refill(…​)

addBalls(…​)

 — 

 — 

no

state

change

 — 

Winner

refill(…​)

addBalls(…​)

HasCoin

refill(…​)

addBalls(…​)

NoCoin

refill(…​)

addBalls(…​)

 — 

 — 

no

state

change

 — 

Winner

insertCoin(…​)

SoldDout

insertCoin(…​)

HasCoin

insertCoin(…​)

 — 

 — 

no

state

change

 — 

Winner

ejectCoin(…​)

SoldDout

ejectCoin(…​)

NoCoin

ejectCoin(…​)

The table reiterates that the state HasCoin needs the most attention in testing. Also note that this table is just the specification, just like the state diagram, and not the implementation.

1. Lets start coding

…​err, writing our tests. To get our State tested without any real machine nearby, we must mock out Context. This can be done by hand, but soon makes us create a context implementation that sort of works, but

  • first of all, is not tested itself, thus violates the TDD workflow

  • does not behave the way we need it in the test for the State type

  • and we do not need to have this context to behave in any way other then to give predictable answers when called by the State, and we must ensure that the State has the exact interactions we want. Not too few, and also not too many.

This is where Mockito comes in. We only use a few of its features, namely

  • the mocking facility,

  • recording and playback of method calls and return values

  • verification of the correct calls on the mocked Context.

Our setup looks as follows:

    @Mock (1)
    Context machine; (2)
    StringOutput sout = new StringOutput(); (3)
    PrintStream out = sout.asPrintStream(); (4)

    @Before
    public void setup() {
        MockitoAnnotations.initMocks( this );(5)
        when( machine.getOutput() ).thenReturn( out ); (6)
    }
1 The annotation makes mockito insert a mocked context, that is a class that can be controlled in our tests.
2 Is the type and name of the mock.
3 Creates a StringOutput and
4 ensure that anything output by the context is returned in an easily interpreted String. Also, every test is executed between a setup() and teardown call, if available (In Junit 4, annotated with @Before and @After). This makes inspection of the output easier.
5 Sets the Mockito annotations to do their thing and
6 lets the machine return our crafted out when it calls for it’s PrintStream.

This basic setup is done for every test anew and makes sure our SUT has a fresh collaborator every time, ignorant of what happened in earlier tests.

1.1. First test

Let us write out first test: Make sure the machine does not return any ball(s) when it’s state is NoCoin. From the [state_diagram] we learn that the action draw() should only be effective in the state HasCoin. This gives the interaction, modeled in the sequence diagram below.

Draw

As can be seen in the sequence diagram, the dispense() call is only allowed in the state HasCoin, not in NoCoin. Let the test verify that using a mockito verify. A Mockito verify is similar to a JUnit test, it will complain (in Java: throw an Assert Exception), when the right things are NOT happening.

    @Test
    public void test_no_quarter_draws_nothing() {
        System.out.println( "test no dice" );
        State state = NoCoin;  (1)
        state.draw( machine );  (2)
        String response = sout.toString(); (3)
        // assert response
        System.out.println( "response 1 = " + response );
        assertTrue( response.startsWith( "You must put in a coin" ) ); (4)
        // verify that machine.setState is not called
        verify( machine, never() ).setState( Matchers.any() ); (5)
        verify( machine, never() ).changeState( Matchers.any() ); (6)
        verify( machine, never() ).dispense(); (7)
    }
1 Select the appropriate instance of the SUT.
2 Invoke the interaction
3 Capture the response
4 JUnit assert the appropriate response
5 Ensure no state change happened (we are still looking at NoCoin), using a Mockito verify(…​).
6 and
7 Verify that the dispense() method is not called.

1.2. And then some

Work through the remainder of the tests.

  • test_no_quarter_draws_nothing() Verify that dispense() is NOT called and state is not changed.

  • test_one_coin_dispenses_ball() Verify that dispense() is called and state is changed to NoCoin.

  • test_winner_dispenses_ball() Verify that winner dispenses a ball and leaves the machine empty.

  • test_one_draw_to_winner() Verify that the guard isWinner() == true leads to state Winner and that there is also a dispense() call.

  • test_winner_takes_all() Winner will take last ball. (Guess).

  • test_winner_not_so_gready() Winner leaves a ball. (Guess).

  • test_one_ball_to_sold_out() Make sure that isEmpty() is called and next state is SoldOut when isEmpty returns false.

  • insert_no_coin_to_has_coin() Does inserting a coin have the proper effect?

  • eject_has_coin_to_no_coin() Does ejecting the coin have the proper effect?

  • refill_back_to_business() Does refill make the machine 'Betriebsbereit'?

  • fill_it_up_in_every_state() What happens when you refill in all states. Make sure only SoldOut changes state to NoCoin and in all other cases the state of the context is 'unaffected'.

  • testValues() To see if all states are there. (Coverage)

  • testValueOf() Ih the name translation works. (Coverage)

1.3. Integrate with the real Context, the OlifantysBallMachine

There are strong advantages when using enum as a state implementation:

  • There is no chance of creating a new state. Enum ensures that the only instances there ever will be are created at class loading time.

  • The instances can be shared between contexts, because the States do no have any state. The state is saved in the context, remember. This is not because enums cannot have state, they can, but because a state in a state-machine shouldn’t. It IS the state and should not have any. More importantly, they are immutable, meaning no one can break them and set the State into some wrong state.

  • The state is easily tested without a need for a complex context. It can easily be mocked as we have seen in the earlier test.

Of course, by just testing the states, you are not yet done. You must also test the context and see if that uses the states properly and as intended.

As an example we code one of the more interesting use cases is: When I insert a coin in any state, ensure that only state NoCoin leads to a new State (HasCoin).

In this test we use Mockito once more, now not just to mock things, but to spy instead. The whole setup and testing can take place in one test method.

    /**
     * Use a spy to monitor what happens in a instance and if indeed the proper
     * methods a are being called. We call the instance buggedInstance to show
     * that it is being watched.
     */
    @Test
    public void make_me_rich_aka_try_insert_coin_in_every_state() {
        OlifantysBallMachine m = new OlifantysBallMachine();
        OlifantysBallMachine buggedInstance = Mockito.spy( m ); (1)
        buggedInstance.refill( 5 );
        buggedInstance.insertCoin(); (2)
        buggedInstance.insertCoin(); (3)
        verify( buggedInstance, times( 1 ) ).setState( HasCoin ); (4)
        assertSame( "should be stubbornly staying in HasCoin", HasCoin,
                buggedInstance.getState() ); (5)
    }
1 First we make SUT instance, then 'bug' it, as in give it to a spy to do its thing with it. Now we interact with the SUT through the spy in
2 insert a coin
3 and again
4 which should only result in one state change,
5 namely in this state. (You could argue that this assertSame(…​) is redundant and you would be right. Here I do this to show that spying supports both invocation verification as well as the normal JUnit stuff.)

1.3.1. A state machine should ignore event/methods

Looking at the [StateTable], we see a lot of method method calls that do not result in a state change (all the empty cells in the End state column). It looks a tedious job to write all these tests, but we might be able to use a Java 8 trick here: Why not pass in all methods as method references that should be ignored to a helper inside the test class.

Writing this helper is trivial. We know nothing about how the methods work, so why not take the simplest Functional interface of all as their template, Runnable.

The helper would now look like this:

    static void assertNoStateChangeSpied( Context ctx, Runnable... m ) {
        State initial = ctx.getState();
        for ( Runnable runnable : m ) {
            runnable.run();
            verify( ctx, never() ).setState( any() );
            verify( ctx, never() ).changeState( any() );
            assertSame( "invocation effected state change", initial, ctx.
                    getState() );
        }
    }

and used like this

    /**
     * Test that methods that should not result in a state change indeed do not.
     * Use a helper method to invoke the methods as Runnable.
     */
    @Test
    public void ignored_events_in_winner() {
        instance.setState( Winner );
        OlifantysBallMachine spy = Mockito.spy( this.instance ); (1)
        assertNoStateChangeSpied( spy, (2)
                  spy::insertCoin, (3)
                  spy::ejectCoin, (4)
                  () -> { spy.refill( 2 ); } (5)
        );
    }

where

1 Tells the spy what to spy on
2 which is passed as the context, as in the spy impersonates the context.
3 is simply passing the method reference because it fits the Runnable function shape, so does
4 and
5 wraps the method call in a wrapper with a Lambda taking nothing and returning nothing, same as Runnable does.

2. Test Driven, is that sufficient?

After completing the initial implementation, let us look at some other qualities of our code and tests.

2.1. How about coverage

Demo jacaco JAva COde COverage with the Tikione NetBeans IDE plugin .

First of all: did we test all there is to test? Is there some unchartered code, not tested? This is particularly bad in code that should model or implement behavior. Let us make sure we tested at least all we have in our enum and maybe interface in the State hierarchy too.

2.2. Design issues.

The initial design suggests to use interfaces both for the context (the automaton or state machine proper) and the state. Doing this often results in too big AN API, because it exposes methods about state handling, which never should be accessible by the client application, outside the package.

What can we do?

  • Make the State package private.

  • Make sure that the State does not occur as parameter or return value in public methods of the state machine. Since we started with the context design as an interface, we must do better.

    • Collapse the hierarchy, make enum (private) inner of state machine?

    • What is the effect on the methods (example cheating isWinner()).

The solution I chose is that of making the Context into an abstract class, which gives more control over the method’s visibility than an interface does. I took out public on all methods that should not have relevance to any client of this implementation. I also made the State-interface package private and ensured that the state objects are not leaked in or out of the package (No protected or public on methods that have State as parameter or return type).

Other solutions are possible too of course.