ModelJUnit is a Java library that extends JUnit to support model-based testing.

In this example, we will consider how to model and test a simple vending machine. The system under test is illustrated by the following state diagram:

The VendingMachineModel class models this system. Our script makes use of the RandomTester class to generate some tests for this model. The total code looks like this:

// require junit.jar
// require modeljunit.jar

import net.sourceforge.czt.modeljunit.*
import net.sourceforge.czt.modeljunit.coverage.*

class VendingMachineModel implements FsmModel {
    def state = 0 // 0,25,50,75,100

    void reset(boolean testing) {state = 0}

    boolean vendGuard() {state == 100}
    @Action void vend() {state = 0}

    boolean coin25Guard() {state <= 75}
    @Action void coin25() {state += 25}

    boolean coin50Guard() {state <= 50}
    @Action void coin50() {state += 50}
}

def tester = new RandomTester(new VendingMachineModel())
tester.buildGraph()
tester.addListener "verbose"
tester.generate 20

When we run this script, we will see the randomly generated test transitions. The output will be:

done (0, coin50, 50)
done (50, coin25, 75)
done (75, coin25, 100)
done (100, vend, 0)
done Random reset(true)
done (0, coin25, 25)
done (25, coin50, 75)
done (75, coin25, 100)
done (100, vend, 0)
done (0, coin25, 25)
done (25, coin50, 75)
done (75, coin25, 100)
done (100, vend, 0)
done (0, coin50, 50)
done (50, coin50, 100)
done (100, vend, 0)
done (0, coin25, 25)
done (25, coin50, 75)
done (75, coin25, 100)
done (100, vend, 0)

We can optionally obtain additional metrics about the test:

// ... as before to create the tester

def metrics = [
    new ActionCoverage(),
    new StateCoverage(),
    new TransitionCoverage(),
    new TransitionPairCoverage()
]
metrics.each {
    tester.addCoverageMetric it
}

tester.addListener "verbose"
tester.generate 20

println 'Metrics Summary:'
metrics.each {
    tester.model.printMessage "$it.name was $it"
}

This generates the following:

Metrics Summary:
Action Coverage was 3/3
State Coverage was 5/5
Transition Coverage was 7/8
Transition-Pair Coverage was 9/12

We can also add a graph listener with the following code:

def graphListener = tester.model.getListener("graph")
graphListener.printGraphDot "vending.dot"
println "Graph contains " + graphListener.graph.numVertices() +
        " states and " + graphListener.graph.numEdges() + " transitions."

When run, a graphviz dot file is produced (corresponding to the image above) and the output is:

Graph contains 5 states and 8 transitions.

See also: State Pattern