In RPGUnit, a test case is an exported procedures that starts with the name test. This also means that you should not export procedures starting with test if they are not intended to be a test case.
Listed below are some example test case names that follow this naming convention:
test_getEmployeeDetail
test_getDeptDetail
testIsPalindrome
testFactorial
Test Suites
A test suite consists of one or more test cases in a file or source member. This test suite will be compiled into a service program in order to run the test cases. Although RPGUnit itself does not enforce an specific naming convention for test suites, the IBM i Testing extension does based on whether you are working on local files on your PC or source members on the IBM i.
Local Files: The file name must end in .test.rpgle, .test.sqlrpgle, .test.cblle, or .test.sqlcblle in order to be detected by the extension. File names are also not restricted to 10 characters as the extension will use the same rules that Source Orbit uses to handle long file names when compiling the test. Noteably, since all files must include .test, the resulting object name will start with T. These files can also be put in any directory.
Source Members: Source members can be named freely, but it is recommended to use some sort of prefix or suffix (ie. T, _T, T_, etc.) to avoid naming conflicts as the tests will be compiled into service programs. These source members can be put in any source file, but it is recommended to store them in a source file named QTESTSRC. By default this is where the extension will look for test suites in your library list. You can configure the extension to search other source files by going to the extensionβs settings and searching for the Test Source Files setting.
Listed below are some example test suite names that follow this naming convention:
Local Files
employee.test.rpgle
department.test.rpgle
string.test.rpgle
math.test.rpgle
Source Members
QTESTSRC source file with members:
EMPLOYEET.RPGLE
DEPARTMENT.RPGLE
T_STRING.RPGLE
MATH_T.RPGLE
Generating Test Stubs
To simplify the process of writing tests, you can have the extension generate stubs for individual test cases or an entire test suite. To do this, place your cursor inside any export procedure and click the code action button (π‘). This will give you an option to generate a test case for the specific procedure you are in or to generate a test suite containing test cases for every procedure.
After selecting an option, a test suite will be created if it does not already exist with a scaffold that will include not only the test case stubs, but also the necessary includes and prototypes (if necessary). The generated test case stubs themselves will each be structured into the following sections: declarations, inputs, actual results, expected results, and assertions. As the developer, you will need to fill in the inputs and expected results.
API Reference
In order to use the APIs provided by RPGUnit, you need to add the TESTCASE copybook to your test suite:
Used to set up a test suite before the first test case is started.
This procedure can be used to set up test data or allocate resources before the first test case is started.
Syntax
setUpSuite();
Example
dcl-proc setUpSuite export;
// Setup test suite code here
end-proc;
Set Up Test Case
Used to set up a test case before it is executed.
This procedure is called for each test case.
Syntax
setUp();
Example
dcl-proc setUp export;
// Setup test case code here
end-proc;
Tear Down Test Suite
Used to tear down a test suite after the last test case has been executed.
This procedure can be used to remove test data or deallocate resources after the last test case has been executed.
Syntax
tearDownSuite();
Example
dcl-proc tearDownSuite export;
// Tear down test suite code here
end-proc;
Tear Down Test Case
Used to tear down a test case after it has been executed.
This procedure is called for each test case.
Compares the given expected and actual values (can be a string, numeric, float, date, time, timestamp).
The assertion fails, if both values are different. If the message parameter is specified, it is appeneded to the log.
Asserts that the given actual value satisfies the condition specified by the given matcher.
Calls the matcher for comparing actual. The assertion fails if the mathcer return false.
If the message parameter is specified, it is appeneded to the log.
Syntax
assertThat( expected : actual : matcher [: message );
Example
assertThat( actual : %paddr('matcher') : pUserData );
Boolean Equality Assertion
Compares the given expected and actual Boolean values. The assertion fails, if both values are different.
If the fieldName parameter is specified, the message is prefixed with fieldName:.
Syntax
nEqual( expected : actual [: 'fieldName' );
Example
// β Pass
nEqual( *off : 1=2 );
// β Fail
nEqual( *on : 'ABC'='DEF' );
Truthy Assertion
Checks if the specified Boolean expression is true. The assertion fails if the expression evaluates to false.
When the assertion fails, the value of message is added to the test report.
Syntax
assert( booleanExpression : 'message' );
Example
// β Pass
assert( 1=1 : 'Impossible to fail' );
// β Fail
assert( 1=0 : 'Obviously wrong' );
Fail
Produces an error and appends the specified message to the test report. The test case is terminated.
Syntax
fail( 'message' );
Example
monitor;
produceAnException(doFailure);
// β Fail: Only runs if exception was not caught
fail('produceAnException(..) should have raised an error.');
on-error;
// β Pass: Only runs if exception was caught
endmon;
Job Log Contains Assertion
Checks whether the job log contains the specified message ID between NOW and timeLimit. The value of timeLimit should be created with the getFullTimeStamp() utility.
Checks whether a given message queue contains the specified message between NOW and timeLimit.
The message is identified by any combination of ID, message text or message help where unused selection parameters must be omitted.
Message ID can be set to *EMPTY to test whether a message queue is empty (all other parameters must be omitted).
Message text and message help can be specified as generic strings values.
The value of timeLimit should be created with the getFullTimeStamp() utility.
Compares the given expected and actual String values. The assertion fails, if both values are different.
If the fieldName parameter is specified, the message is prefixed with fieldName:.
Syntax
aEqual( 'expected' : 'actual' [: 'fieldName' );
Example
// β Pass
aEqual( 'Hello' : 'Hello' );
// β Fail
aEqual( 'HelloWorld' : 'Hello' );
Integer Equality Assertion
Compares the given expected and actual Integer values. The assertion fails, if both values are different.
If the fieldName parameter is specified, the message is prefixed with fieldName:.
Syntax
iEqual( expected : actual [: 'fieldName' );
Example
// β Pass
iEqual( 123 : 123 );
// β Fail
iEqual( 123 : 456 );
Boolean Equality Assertion
Compares the given expected and actual Boolean values. The assertion fails, if both values are different.
If the fieldName parameter is specified, the message is prefixed with fieldName:.
Syntax
nEqual( expected : actual [: 'fieldName' );
Example
// β Pass
nEqual( *off : 1=2 );
// β Fail
nEqual( *on : 'ABC'='DEF' );
Truthy Assertion
Checks if the specified Boolean expression is true. The assertion fails if the expression evaluates to false.
When the assertion fails, the value of message is added to the test report.
Syntax
assert( booleanExpression : 'message' );
Example
// β Pass
assert( 1=1 : 'Impossible to fail' );
// β Fail
assert( 1=0 : 'Obviously wrong' );
Fail
Produces an error and appends the specified message to the test report. The test case is terminated.
Syntax
fail( 'message' );
Example
monitor;
produceAnException(doFailure);
// β Fail: Only runs if exception was not caught
fail('produceAnException(..) should have raised an error.');
on-error;
// β Pass: Only runs if exception was caught
endmon;
Job Log Contains Assertion
Checks whether the job log contains the specified message ID between NOW and timeLimit. The value of timeLimit should be created with the getFullTimeStamp() utility.
Checks whether a given message queue contains the specified message between NOW and timeLimit.
The message is identified by any combination of ID, message text or message help where unused selection parameters must be omitted.
Message ID can be set to *EMPTY to test whether a message queue is empty (all other parameters must be omitted).
Message text and message help can be specified as generic strings values.
The value of timeLimit should be created with the getFullTimeStamp() utility.
Returns the full current timestamp, without rounding the microseconds like %timestamp() does.
Syntax
getFullTimeStamp();
Return value
timestamp
Example
tmStmp = getFullTimeStamp();
Wait Seconds
Suspends the current job for a specified number of seconds. Optionally displays a status message.
When the job resumes the original status message is restored.
Syntax
waitSeconds( seconds [: message )
Return value
void
Example
waitSeconds(3 : 'Waiting 3 seconds ...');
Get Monitored Message
Retrieves the latest *ESCAPE message from the job log. Usually called within the on-error section of a monitor block.
Syntax
getMonitoredMessage( [: doRmvMsg );
Return value
Returns a data structure with the following information:
id - Message ID
text - First level text
pgm - Sender: program name
mod - Sender: module name
proc - Sender: procedure name
specNb - Sender: statement number
Example
monitor;
a = 10;
b = 0;
c = a / b;
// β Fail: Only runs if exception was not caught
fail('Division by zero did not raise an error.');
on-error;
msgInfo = getMonitoredMessage(*ON);
endmon;
// β Pass
aEqual( 'MCH1211' : msgInfo.Id );
Set Low Message Key
Sets the message key of the oldest message in the job log, that is considered to be returned by getMonitoredMessage().
This procedure is automatically called by the RPGUnit, before executing a unit test. Use this procedure with care.