This package provides comprehensive predefined mocks for the AXUnit testing framework. It includes simple timer mocks as well as advanced configurable mocks for complex testing scenarios with multiple timers.
apax add @simatic-ax/mocks --dev
Simatic.Ax.Mocks;
- You have a single timer in your function block, OR
- All timers should have the same state (all elapsed or all waiting)
For testing multiple timers with different states, use ConfigurableOnDelayMock or IdentifierBasedOnDelayMock.
| Name | Output | ElapsedTime | Use Case |
|---|---|---|---|
OnDelayMock_false |
FALSE | 0ms | Timer not elapsed |
OnDelayMock_true |
TRUE | duration | Timer elapsed |
OffDelayMock_false |
FALSE | 0ms | Timer not elapsed |
OffDelayMock_true |
TRUE | duration | Timer elapsed |
| Name | Q Output | ET | Use Case |
|---|---|---|---|
TON_Mock_false |
FALSE | 0ms | On-Delay not elapsed |
TON_Mock_true |
TRUE | PT | On-Delay elapsed |
TOF_Mock_false |
FALSE | 0ms | Off-Delay not elapsed |
TOF_Mock_true |
TRUE | PT | Off-Delay elapsed |
Example - Single Timer (✅ Correct Usage):
FUNCTION_BLOCK SingleTimerController
VAR
startupTimer : OnDelay;
END_VAR
startupTimer(signal := enable, duration := T#5s);
END_FUNCTION_BLOCK
{Test}
METHOD PUBLIC Test_StartupComplete
// ✅ CORRECT: Only one timer - simple mock works perfectly
AxUnit.Mocking.Mock(NAME_OF(OnDelay), NAME_OF(OnDelayMock_true));
controller(enable := TRUE);
Equal(expected := TRUE, actual := controller.isReady);
END_METHOD
Example - All Timers Same State (✅ Correct Usage):
FUNCTION_BLOCK MultiTimerAllSame
VAR
timer1, timer2, timer3 : OnDelay;
END_VAR
// All timers must be elapsed
IF timer1.output AND timer2.output AND timer3.output THEN
allReady := TRUE;
END_IF;
END_FUNCTION_BLOCK
{Test}
METHOD PUBLIC Test_AllTimersElapsed
// ✅ CORRECT: All timers need same state (all TRUE)
AxUnit.Mocking.Mock(NAME_OF(OnDelay), NAME_OF(OnDelayMock_true));
controller(enable := TRUE);
Equal(expected := TRUE, actual := controller.allReady);
END_METHOD
Example - Mixed Timer States (❌ Wrong Usage):
FUNCTION_BLOCK MixedTimerStates
VAR
timer1, timer2, timer3 : OnDelay;
END_VAR
// Need: timer1=TRUE, timer2=FALSE, timer3=TRUE
END_FUNCTION_BLOCK
{Test}
METHOD PUBLIC Test_MixedStates
// ❌ WRONG: Simple mocks cannot create mixed states!
// All timers will be TRUE or all FALSE
AxUnit.Mocking.Mock(NAME_OF(OnDelay), NAME_OF(OnDelayMock_true));
// ✅ SOLUTION: Use ConfigurableOnDelayMock or IdentifierBasedOnDelayMock instead!
END_METHOD
📖 See SimpleMocks_RealWorld_Test.st for complete examples.
Best for: Sequential timers with consistent call order and different states
- Supports up to 4 timer instances with individual states
- Call-order based identification (1st call = T1, 2nd call = T2, etc.)
- Works with timers having identical durations
- Simple configuration
- Available for: OnDelay, OffDelay, TON, TOF
Available Mocks:
ConfigurableOnDelayMock+ConfigurableOnDelayMockPayload- For OnDelayConfigurableOffDelayMock+ConfigurableOffDelayMockPayload- For OffDelayConfigurableTONMock+ConfigurableTONMockPayload- For TONConfigurableTOFMock+ConfigurableTOFMockPayload- For TOF
payload.ResetCounter();
// Timer 1: elapsed
payload.T1_Enabled := TRUE;
payload.T1_Output := TRUE;
payload.T1_ElapsedTime := T#1s;
// Timer 2: waiting (different state!)
payload.T2_Enabled := TRUE;
payload.T2_Output := FALSE;
payload.T2_ElapsedTime := T#500ms;
// Timer 3: elapsed
payload.T3_Enabled := TRUE;
payload.T3_Output := TRUE;
payload.T3_ElapsedTime := T#5s;
AxUnit.Mocking.Mock(
mockeeFn := NAME_OF(OnDelay),
mockFn := NAME_OF(ConfigurableOnDelayMock),
payload := payload
);
Best for: Conditional timer logic (e.g., IF mode=1 THEN timer2 ELSE timer3)
- Supports up to 4 timer instances with individual states
- Position-based identification with explicit call positions
- Perfect for conditional timer usage
- Duration-independent
- Available for: OnDelay, OffDelay, TON, TOF
Available Mocks:
IdentifierBasedOnDelayMock+IdentifierBasedOnDelayMockPayload- For OnDelayIdentifierBasedOffDelayMock+IdentifierBasedOffDelayMockPayload- For OffDelayIdentifierBasedTONMock+IdentifierBasedTONMockPayload- For TONIdentifierBasedTOFMock+IdentifierBasedTOFMockPayload- For TOF
payload.ResetCounter();
// Position 1: Always called
payload.T1_Enabled := TRUE;
payload.T1_CallPosition := 1;
payload.T1_Output := TRUE;
// Position 2: Called in mode 1
payload.T2_Enabled := TRUE;
payload.T2_CallPosition := 2;
payload.T2_Output := FALSE;
// Position 2: Called in mode 2 (same position, different timer!)
payload.T3_Enabled := TRUE;
payload.T3_CallPosition := 2;
payload.T3_Output := TRUE;
AxUnit.Mocking.Mock(
mockeeFn := NAME_OF(OnDelay),
mockFn := NAME_OF(IdentifierBasedOnDelayMock),
payload := payload
);
USING System.Timer;
USING AxUnit.Assert;
NAMESPACE Simatic.Ax.Mocks
FUNCTION_BLOCK FunctionBlockWhichUsesTimer
VAR_INPUT
enable : BOOL;
END_VAR
VAR_OUTPUT
timerStatus : STRING;
END_VAR
VAR
ton : OnDelay;
END_VAR
ton(signal := enable, duration := T#10s);
IF (ton.output) THEN
timerStatus := 'ELAPSED';
ELSIF (ton.signal AND NOT ton.output) THEN
timerStatus := 'RUNNING';
ELSIF (NOT ton.signal) THEN
timerStatus := 'IDLE';
END_IF;
END_FUNCTION_BLOCK
{TestFixture}
CLASS TestFunctionBlockWhichUsesTimer
VAR
testInstance : FunctionBlockWhichUsesTimer;
END_VAR
{Test}
METHOD PUBLIC FunctionBlockReturnsElapsedWhenEnabledAndTimeHasElapsed
// ✅ CORRECT: Single timer - simple mock is perfect
AxUnit.Mocking.Mock(NAME_OF(OnDelay), NAME_OF(OnDelayMock_true));
testInstance(enable := TRUE);
Equal(expected := 'ELAPSED', actual := testInstance.timerStatus);
END_METHOD
END_CLASS
END_NAMESPACE
{Test}
METHOD PUBLIC TestMultipleTimersWithDifferentStates
VAR
payload : ConfigurableOnDelayMockPayload;
END_VAR
payload.ResetCounter();
// T1: elapsed
payload.T1_Enabled := TRUE;
payload.T1_Output := TRUE;
payload.T1_ElapsedTime := T#10s;
// T2: waiting (different state!)
payload.T2_Enabled := TRUE;
payload.T2_Output := FALSE;
payload.T2_ElapsedTime := T#5s;
// T3: elapsed
payload.T3_Enabled := TRUE;
payload.T3_Output := TRUE;
payload.T3_ElapsedTime := T#30s;
AxUnit.Mocking.Mock(
mockeeFn := NAME_OF(OnDelay),
mockFn := NAME_OF(ConfigurableOnDelayMock),
payload := payload
);
// Test your function block
myFB(enable1 := TRUE, enable2 := TRUE, enable3 := TRUE);
Equal(expected := TRUE, actual := myFB.OutputT1);
Equal(expected := FALSE, actual := myFB.OutputT2); // Different state!
Equal(expected := TRUE, actual := myFB.OutputT3);
END_METHOD
{Test}
METHOD PUBLIC TestConditionalTimers
VAR
payload : IdentifierBasedOnDelayMockPayload;
END_VAR
payload.ResetCounter();
// Position 1: Always called
payload.T1_Enabled := TRUE;
payload.T1_CallPosition := 1;
payload.T1_Output := TRUE;
// Position 2: Called in mode 1 OR mode 2 (different timers, same position)
payload.T2_Enabled := TRUE;
payload.T2_CallPosition := 2;
payload.T2_Output := TRUE; // Mode 1 result
AxUnit.Mocking.Mock(
mockeeFn := NAME_OF(OnDelay),
mockFn := NAME_OF(IdentifierBasedOnDelayMock),
payload := payload
);
// Test mode 1
myFB(mode := 1, enable := TRUE);
END_METHOD
| Scenario | Recommended Mock |
|---|---|
| Single timer | OnDelayMock_true / OnDelayMock_false ✅ |
| Multiple timers, all same state | OnDelayMock_true / OnDelayMock_false ✅ |
| Multiple timers, different states | ConfigurableOnDelayMock ✅ |
| Conditional timer logic (IF/CASE) | IdentifierBasedOnDelayMock ✅ |
| IEC 61131-3 standard timers | TON_Mock_true / TOF_Mock_false ✅ |
Always use the stateless pattern to ensure test isolation:
{TestFixture}
CLASS MyTests
VAR
myFB, myFBStateless : MyFunctionBlock;
payload, payloadStateless : ConfigurableOnDelayMockPayload;
END_VAR
{TestSetup}
METHOD PUBLIC TestSetup
payload := payloadStateless;
myFB := myFBStateless;
END_METHOD
END_CLASS
For call-order based mocks, always reset the counter:
{Test}
METHOD PUBLIC MyTest
payload.ResetCounter(); // ← Essential!
// ... configure and test ...
END_METHOD
- ConfigurableOnDelayMock - Call-order based mock for multiple timers
- IdentifierBasedOnDelayMock - Position-based mock for conditional logic
- TimerMocks-Overview - Complete overview and decision guide
All mocks include comprehensive test coverage. See the test/ directory for examples:
Simple Mocks:
- SimpleMocks_RealWorld_Test.st - Real-world examples showing when to use simple mocks
- IEC_TON_Mocks_Test.st - IEC TON timer tests
- IEC_TOF_Mocks_Test.st - IEC TOF timer tests
- System_OnDelay_Mocks_Test.st - System OnDelay tests
- System_OffDelay_Mocks_Test.st - System OffDelay tests
Configurable Mocks:
- ConfigurableOnDelayMockTest.st - OnDelay configurable mock
- ConfigurableOffDelayMockTest.st - OffDelay configurable mock
- ConfigurableTONMockTest.st - TON configurable mock
- ConfigurableTOFMockTest.st - TOF configurable mock
Identifier-Based Mocks:
- IdentifierBasedOnDelayMockTest.st - OnDelay identifier-based mock
- IdentifierBasedOffDelayMockTest.st - OffDelay identifier-based mock
- IdentifierBasedTONMockTest.st - TON identifier-based mock
- IdentifierBasedTOFMockTest.st - TOF identifier-based mock
Real-World Examples:
- FooFB_ConfigurableMock_Test.st - Real-world configurable example
- FooFB_IdentifierMock_Test.st - Real-world identifier-based example
Thanks for your interest in contributing. Anybody is free to report bugs, unclear documentation, and other problems regarding this repository in the Issues section or, even better, propose changes using Merge Requests.
Please read the Legal information