Sometimes it is necessary to write stateful mocks, which return different values depending on their state. In the following example the function ActiveWait waits until the waitingTime is elapsed and returns true. Therefore, the first call of GetOperatingTime sets the startTime initially.
The elapsed time is reset in a while loop using the startTime and the return value of GetOperatingTime. With each iteration, the return value of GetOperatingTime is incremented.
FUNCTION ActiveWait : BOOL
VAR_INPUT
waitingTime: LTIME;
END_VAR
VAR_TEMP
elapsedTime : LTIME;
startTime : LTIME;
ticks : INT := 0;
END_VAR
startTime := GetOperatingTime();
WHILE elapsedTime < waitingTime DO
elapsedTime := GetOperatingTime() - startTime;
ticks := ticks + 1;
END_WHILE;
ActiveWait := TRUE;
END_FUNCTION
To be able to write a unit test for ActiveWait, the function GetOperatingTime must be mocked. For such scenarios AxUnit offers the possibility to implement stateful mocks, which are able to return values depending on their state. Therefore, the function AxUnit.Mocking.Mock<mockee, mock, IPayload> accepts an optional third parameter. The optional parameter must implement the interface IPayload. The payload can be used inside of the mock. The class TestActiveWait uses this technique and passes the state variable to the GetTickMock.
{TestFixture}
CLASS TestActiveWait
VAR
state : GetTickMockState;
END_VAR
{Test}
METHOD PUBLIC Should_reached_waiting_time
AxUnit.Mocking.Mock('Siemens.Simatic.S71500.Clocks.GetOperatingTime', NAME_OF(GetTickMock), THIS._state);
AxUnit.Assert.Equal(TRUE, ActiveWait(LTIME#5s));
END_METHOD
END_CLASS
Inside GetTickMock, the GetPayload() function is used to unbox the state. The state is incremented by 10 each time the mock is called. The result is saved in the instance of TestActiveWait. In this example the GetTickMock returns: 0,10,20,30,40,50 and the test TestActiveWait passes.
FUNCTION GetTickMock : LTIME
VAR_TEMP
state : REF_TO GetTickMockState;
END_VAR
state ?= AxUnit.Mocking.GetPayload();
state^.value := state^.value + T#10ms;
GetTickMock := state^.value;
END_FUNCTION
CLASS GetTickMockState IMPLEMENTS AxUnit.Mocking.IPayload
VAR PUBLIC
value : LTIME := T#0s;
END_VAR
END_CLASS
Note
It is crucial for AxUnit that the payload is provided by the mock setup. Otherwise, AxUnit.Mocking.GetPayload() will return null.