This section introduces SCTUnit by a simple statechart example that models a light switch. We will develop this statechart step by step using a test-driven approach. That is, for each single development step, we will first write a test, then change our statechart model, then run all the tests we created so far until they succeed. After that, we will proceed with the next development step.
Before implementing any tests or models, let’s write down the requirements for our light switch statechart model:
Now we need an Eclipse project to host our statechart model. So let’s create one, naming it light_switch. Within the project, we create two folders, model and test, for – you guessed it – the model and the tests, respectively.
In the test directory, proceed as follows to create a SCTUnit test file:
As a result, the empty file light_switch.sctunit is created in the test folder and is opened in an editor.
In the spirit of test-driven development, create a test for the light switch model’s initial behaviour by writing the following in the light_switch.sctunit file:
testclass light_switch_tests for statechart light_switch {
@Test
operation isStateOffActiveAfterInit () {
enter
assert active (light_switch.main_region.Off)
}
}
Let’s walk through this example line by line and see what all of this means.
testclass light_switch_tests for statechart light_switch {
testclass
introduces a test class, a collection of tests. In this example, it is named
light_switch_tests.
for statechart
are followed by the name of the statechart, here:
light_switch. However, since there is no
light_switch statechart yet, an error marker is attached to the statechart name, as shown in the following screenshot:{
and
}
.
@Test
operation isStateOffActiveAfterInit () {
@Test
. This example defines the operation
isStateOffActiveAfterInit. It has no parameters, which is indicated by ()
. As we will see later, an operation does not necessarily have to be a test, but can be a mere subroutine. Such operations can have parameters. The operation’s body is bracketed in braces.
enter
assert active (light_switch.main_region.Off)
Since the test has error markers, we can say that it fails even without being run. So let’s fix that with minimal effort and create a model that is called
light_switch and has a state
Off. Also, change the annotation
EventDriven
to
CycleBased(200)
. Figure
"First step of creating a light switch model" shows the resulting model.
First step of creating a light switch model
Figure "First step of creating a light switch model" also shows that the error markers of the test class have been cleared, because our minimal statechart model has the name that is specified in the test and it also contains the Off state.
Now we can execute the test class. In order to do so, proceed as follows:
The model by now implements the first requirement specified in section "Light switch requirements".
So let’s take care of the second requirement, formulate it as a SCTUnit test, and add it to the test class.
The whole test class now looks like this:
testclass light_switch_tests for statechart light_switch {
@Test
operation isStateOffActiveAfterInit () {
enter
assert active (light_switch.main_region.Off)
}
@Test
operation isStateOnActiveAfterOperateInOffState () {
enter
raise operate
proceed 1 cycle
assert active (light_switch.main_region.On)
}
}
The
raise operate
instruction raises the
operate event. However, aside from this event having been raised, nothing will happen yet. The next instruction will change that.
The
proceed 1 cycle
instruction orders the state machine to execute one run-to-completion step (RTC). During this RTC, the state machine can react to the raised
operate event.
Processing the
operate event should cause the
On state to become active. This is checked by the following assertion:
assert active (light_switch.main_region.On)
To make the test class syntactically correct, the statechart model must be amended by the operate event and by the On state.
However, in order to demonstrate what happens if an SCTUnit test fails, let’s have a look at a wrong implementation, as shown in figure "Erroneous light switch model". Here the operate event does not trigger a transition from Off to On but from Off back to Off.
Erroneous light switch model
The test operation isStateOnActiveAfterOperateInOffState should detect this semantic error and alert us. Running the amended test class as an SCTUnit again indeed leads to the following result:
The large bar is red now instead of green, meaning that at least one test failed. Below the bar the test class and its tests are listed and it is indicated which tests succeeded and which tests failed. We can see that our isStateOffActiveAfterInit test still succeeds; so while we didn’t correctly implement the second requirement, we at least didn’t break the first one.
Fixing the model as follows turns the test green:
Okay, so let’s write a test for the third and final requirement:
@Test
operation isStateOffActiveAfterOperateInOnState () {
isStateOnActiveAfterOperateInOffState
raise operate
proceed 1 cycle
assert active (light_switch.main_region.Off)
}
A prerequisite for dealing with an operate event in the On state is to get into that state in the first place. However, we have implemented the necessary actions before, namely in the test isStateOnActiveAfterOperateInOffState. The great thing is that we can just use that test by calling it as subroutine. This is like calling a function or method in any other programming language.
We know that after executing isStateOnActiveAfterOperateInOffState the On state is active – we even assert that in the called test operation. So now we just have to raise operate again, cycle the statechart once and check whether we are in the Off state after that.
By going from Off to On and back to Off, we have ensured now that the light switch model behaves as specified. However, we might want to check whether this is still the case if we go through this cycle a number of times, say twice or 20,000 times. To support scenarios like this, the SCTUnit language provides a couple of advanced features, see section The SCTUnit language. Here we use the while loop to iterate over the off-on-off cycle a couple of times:
@Test
operation isStateOffActiveAfter10Cycles () {
enter
var i: integer = 0
while (i < 10) {
raise operate
proceed 1 cycle
assert active (light_switch.main_region.On)
raise operate
proceed 1 cycle
assert active (light_switch.main_region.Off)
i = i + 1
}
}