I have a function that implements a state machine, test_hw(), i e. First state is idle, and it has not calling any mocked function inside. But this machine has 6 states more. The idea is that machine goes from first state to the last sequently, when certainly condition specially, timer result conditions, occures. In the test I have only one mocked function that returns a time value. But this function must be called at every state as a part of condition to return the next state value. It also will be called inside of some states implementation.
First I tried to test every state as a test case. After run it, too much fails the test have found. I started to think that testing states machine will be a special testing case. I was following a TDD book but it doesn't talk about testing machine states.
The second thing I tried was testing all the test_hw machine as a only test case. But it failed again because test intercept more than 6 calls from source code and the test has only 6 (one call to this mocked function per state in test code). I don't understand why source code (production code) call it more than one time per state. But this is what happens. I guess due this is not the correct policy for unit testing a state machine.
I will share with you a reduced state machine based on mine one:
eError TestHW()
{
static unsigned char last_state = FMINUS_DONE; //IDLE state
......... //more needed var declaration
if (FLAG_busy==OFF)
{
if (last_state==FMINUS_DONE) /*IDLE */
{
...... //some assignations
timerfcentral=CaptureTimer(); //timer function to be mocked in test mode
last_state= FCENTRAL_COUNT;
}
if ((CaptureTimer()-timerfcentral>=STABLISH_TIME) && (last_state==FCENTRAL_COUNT)) //timer function call to be mocked in test mode
{ //next state
last_state =FCENTRAL_DONE;
......//some assignments
}
else if (last_state ==FCENTRAL_DONE)
{ //next state
timerfplus=CaptureTimer(); //timer function to be mocked in test mode
last_state= FPLUS_COUNT;
.....//some assignments
}
else if ((last_state ==FPLUS_COUNT) && (CaptureTimer()-timerfminus>=STABLISH_TIME)) //timer function to be mocked in test mode
{
.....//some assignments
last_state = FMINUS_DONE;
}
return last_state;
}
else //FLAG_busy=1
{
last_state= FMINUS_DONE; //machine state loop is closed
return last_state; /* busy system */
}
}
test code:
TEST(TestHW,TestHW_main)
{
unsigned char error_val;
FLAG_busy =1;
error_val=TestHW();
CHECK_EQUAL(error_val,FMINUS_DONE); //check busy state
mock().enable();
FLAG_busy =0;
mock().expectOneCall("CaptureTimer").andReturnValue(1000);
error_val=TestHW();
CHECK_EQUAL(error_val,FCENTRAL_COUNT) /*check state idle*/
mock().expectOneCall("CapturaTimer").andReturnValue(15000);
error_val=TestHW();
CHECK_EQUAL(error_val,FCENTRAL_DONE);//check first state
mock().expectOneCall("CaptureTimer").andReturnValue(15000);
error_val=TestHW();
CHECK_EQUAL(error_val,FPLUS_COUNT);//check second state
mock().expectOneCall("CaptureTimer").andReturnValue(30000);
error_val=TestHW();
CHECK_EQUAL(error_val,FMINUS_DONE);//check last state
mock().disable();
}
mocked timer function implementation in Mock.c file:
unsigned long CaptureTimer(void)
{
mock().actualCall("CaptureTimer");
return mock().unsignedIntReturnValue();
}
As I said before, this test fails due to "second not expected mocked call is produced".
What could be a correct strategy? --> if no test cases for every state, nor only one test entity, which one? Hence, is there any suggestion about the way of implementing a test code for the state machine shown above?
Could someone improve this test code?