Implementing a multitest POU
To implement many related test cases on only one test POU, you can use the pragma {attribute 'test' := 'multitest'}
, which decorate a test POU with ETrigA
behavior with this pragma. Additional information is reported when logging during the test run. For example, the test result for each individual test case is reported and logged. Additional inputs and outputs are required for this.
Creating a framework for the test case
A test case is a function block which follows a specific scheme (Interface convention). To make it easier to follow the scheme, we recommend using the wizard. If you manually create a test case, then it should always derive from TM.BaseMultiTest
. (Obsolete Test POU Implementations).
Add the test case function block below an application or in the POU pool. To do this, click Add Object → Unit Test in the context menu.
The Add Unit Test dialog opens.
Specify the following parameters:
Name of the POU:
TestCase_1
Type: Extend TM.BaseMultiTest
Add optional methods
Add implementation template
Add explanatory comments
Test Case Category:
BasicTests
Click Add to confirm the dialog and save the project.
A new function block is created with the
Execute
,Setup
,Teardown
, andAbort
methods.
For more information, see: Add Unit Test
Implementing the test logic
In the declaration part of the test POU, declare an instance of the testee. The example tests a CTU standard POU.
{attribute 'test':='multitest'} {attribute 'testcasetimeout':='15000'} FUNCTION_BLOCK CTU_TestCases EXTENDS TM.BaseMultiTest VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR counter : CTU; // testee instance cycleCounter : INT; // independant test control variable END_VAR
In the implementation part of the
prvStart
method, initialize the testee. The example initializes the CTU via reset and, if necessary, initializes variables which are required for the test flow. TheprvStart
method is called one time beforeprvCyclicAction
.SUPER^.prvStart(); // mandatory - don't change // Initialize the testee counter(CU:=FALSE, RESET:=TRUE); // reset the counter; note: initialization per test case must be performed in prvCyclicAction // Initialize test control variable for first test case cycleCounter := 0;
Call the testee and check its result in the
prvCyclicAction
method. TheprvCyclicAction
method is called untilxDone
is set toTRUE
. As a result, the testee can be called over multiple PLC cycles for each test case. The following example implements only one test case (CASE 0
), but other test cases can easily be added according to this scheme.SUPER^.prvCyclicAction(); // mandatory - dont' change CASE diTestCaseIndex OF 0: // test case #0; add test cases as new CASEs IF xGetTestInfo THEN // initializate test case // define mandatory test case information wsTestCaseName := "Count from 0 to 5"; wsTestCaseCategories := "CTU"; diTestCaseTimeout := 500; xDone := TRUE; // signal test case initialization done ELSE IF (cycleCounter = 0) THEN // prepare test case counter(CU:=FALSE, RESET:=TRUE, PV:=5); // stimulate testee as long as we expect: 5 rising edges to count to 5 equals minimum 10 cycles ELSIF (cycleCounter < 10) THEN // check: counter limit not reached yet TM.Assert_Bool_IsFalse(THIS^, counter.Q, "Counter signaled limit reached prematurely"); TM.Assert_Word_Less(THIS^, 5, counter.CV, "Counter reached limit unexpectedly"); counter(CU:= (NOT counter.CU), RESET:=FALSE); // Trigger counter with rising edges xDone := FALSE; // signal test still running ELSE // check: counter reached limit TM.Assert_Bool_IsTrue(THIS^, counter.Q, "Counter did not signal limit reached"); TM.Assert_Word_Equal(THIS^, 5, counter.CV, "Counter did not reach limit"); xDone := TRUE; // mandatory: signal test completed END_IF IF xDone OR xError THEN cycleCounter := 0; // clean up for next test case ELSE cycleCounter := cycleCounter + 1; // count cycles END_IF END_IF ELSE IF diTestCaseIndex < 0 THEN // mandatory, signal number of test cases, if called with (diTestCaseIndex < 0) diTestCaseCount := 1; xDone := TRUE; ELSE // mandatory, don't remove: fail for invalid test case index wsInfo := "Invalid test case index"; iError := 2; END_IF END_CASE
Uninitialization of the test could be done in the
prvResetOutputs
method, but it is not necessary in this example. Delete the method.No special code needs to be executed if the test times out. Delete the
prvAbort
method.
Configuring the IEC Unit Test test element
In Title, specify a test case name.
Example:
N_001_Test
When the test is executed, the title is displayed as the test case name.
In Device read timeout (ms), specify a time. Example:
5000
Note: For each test case, the system waits at least for the test status and test result to be read. Moreover, the system waits when the application status is read when logging in.
Before reading variables, the system waits until the timeout has elapsed.
In Login retries, specify the maximum number of attempts to establish online mode. Example:
3
Click in the upper left of the Selected categories table.
An input field is displayed.
Specify all categories which are called in this test case.
Example:
BasicTests
All test function blocks decorated with the
'testcategory' := 'BasicTests'
attribute called in this test case.
Executing a test script
Requirement: The test environment is prepared. The test project is open in the CODESYS Development System and the device settings and communication settings are configured. These required instructions are made by a preparatory test case in the test script.
The contents of the Generated unit tests folder are deleted in order to remove the generated POUs of a previous IEC unit test.
The project and the Libraries to test are searched for test tables. Then the function blocks for executing the test tables are generated in the Generated unit tests folder.
The project and the libraries that are listed on Libraries to test are searched for function blocks and programs that are marked as test.
Then, of the detected programs and function blocks, those are removed which are excluded by the test categories or the name.
The remaining programs and function blocks are used in the test.
A backup of the original application is created.
The test cases are inserted as IEC code in
PLC_PRG
. In the declaration and implementation, the added lines of code are marked withGenerated by IEC Unit Test
.If the application is too large for the controller, then the tests are subdivided automatically into several smaller applications. One or more executable applications are generated.
The test is executed with the generated application(s).
The following steps are processed:
A test application is downloaded to the controller.
The test application is started.
The test cases are executed. If the string variable
sInfo
orwsInfo
is used in the test function blocks, then its contents are included in the test report.The timeout is monitored.
The test result is checked and then sent to the test report.
The application is removed from the controller.
A test report is generated containing all test results.
Tip
Pay attention to the test repository Default-Test-Repository
. The test script Examples.IecUnitTest
is located there. In this test script, you can reconstruct an IEC unit test by loading the prepared steps to the project, updating the device, and configuring the communication settings. This is followed by an IEC unit test. The test cases that are processed originate from the Example_IecUnitTest
project. The project is included in the installation directory: C:\ProgramData\CODESYS Test Manager
.
The sample scripts are created for a CODESYS Control Win controller (32 bit). If you use a different controller, then you need to adapt the parameters of the UpdateDevice
script action.