SCTUnit by example

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.

Light switch requirements

Before implementing any tests or models, let’s write down the requirements for our light switch statechart model:

  1. When the state machine starts, the model is in the Off state (initially the light is off).
  2. If the Off state is active and the operate event occurs (a user operates the light switch), the state machine changes from the Off state to the On state (the light is switched on).
  3. If the On state is active and the operate event occurs (a user operates the light switch), the state machine changes from the On state to the Off state (the light is switched off).

Specifying the initial behaviour as a test

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:

  1. In the project explorer view, right-click on the test folder. The context menu opens.
  2. In the context menu, select New → Other…. The New wizard opens.
  3. In the wizard, select YAKINDU SCT → SCTUnit test case.
  4. Click Next >. The wizard switches to the New SCTUnit Test Class page.
  5. In the Filename text field, specify the name of the file to contain the SCTUnit tests, say, light_switch.sctunit. The filename extension must always be .sctunit.
  6. Click Finish.
  7. If a dialog appears asking whether you want to add the Xtext nature to the project, please click Yes.

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 {
    • The keyword testclass introduces a test class, a collection of tests. In this example, it is named light_switch_tests.
    • A test class always refers to a statechart that is under control and is to be tested. This relationship is established by the for statechart clause. The keywords 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:
      Initial test
    • The body of the test class is bracketed in braces, i.e., between { and }.
  • @Test
    operation isStateOffActiveAfterInit () {
    • Each test is an operation that is annotated by @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
    • The enter statement initializes and starts the state machine; it „enters” it. As a consequence, the state machine activates the state that is pointed to by the initial state.
  • assert active (light_switch.main_region.Off)
    • The assert statement expects that a specified condition is fulfilled. Using the built-in active function, our example’s assertion expects that the state Off is active. The state is addressed in a fully-qualified manner, i.e., statechart. region. state. However, since we don’t have a statechart model yet and less than ever any state, the Off state in the assertion is flagged as an error.

Implementing a minimal light switch model

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

First step of creating a light switch model

Running the test

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:

  • Right-click on the light_switch.sctunit file in the test directory. The context menu opens.
  • In the context menu, select Run As → SCTUnit
    Run as SCTUnit
  • The test in the test class is executed.
  • The results are displayed in the JUnit tab. A green bar is shown, which means our model fulfills the expectations expressed in the test class.
    SCTUnit test succeeded

Adding more tests

Writing a test for the second requirement

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.

Detecting a semantically wrong implementation

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

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:

SCTUnit test failed

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:

Fixed light switch model

Calling operations

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.

Looping

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
        }
    }