How to use axunit

This document describes the 'AX' unit test tool intended to automate all unit tests running with Sage X3 scripts.

Overview

The AXUNIT unit test framework is based on the same fundamentals as 'QUnit' or 'JUnit'.

The main purpose is to have a source file which is a test suite of several test cases that verify some predefined assertions.

File name convention

Every unit test file must match the file name pattern QLF*_*.src. The complete naming rule is described in the How to Naming Rule document.

Unit test template

Writing a unit test follows the template below:
CODECODE CODEsh
Funprog TESTSUITE()
  Call TESTSUITE_START("US-99999-99", "My description of the test suite for User story 99999-99") From AXUNIT
  Call ADD_TESTCASE("TEST_MYCASE1", "My description of the test case 1", NB_CHECKS_IN_TEST_CASE_1) From AXUNIT
  Call ADD_TESTCASE("TEST_MYCASE2", "My description of the test case 2", NB_CHECKS_IN_TEST_CASE_2) From AXUNIT
End func AXUNIT.RUN_TESTSUITE("US-99999-99", "My description of the test suite for User story 99999-99")
Subprog SETUP
#  Optional subprog called *before* running the test suite
End
Subprog TEARDOWN
#  Optional subprog called *after* running the test suite
End
Subprog TEST_MYCASE1
  # ... some code to get the result we want to verify
  # the result of the code will be put to the first parameter of CHECK_EQUAL,
  # the expected value will be put to the second parameter of CHECK_EQUAL
  Call CHECK_EQUAL(GOT, EXPECT) From AXUNIT
  # ... some code to get the result we want to verify
  # the code returns a logical value which we want to check. It will be put into the 
  # first parameter of CHECK_TRUE
  Call CHECK_TRUE(GOT) From AXUNIT
  # ... 
End
Subprog TEST_MYCASE2
  # ... 
End
Example: We want to write unit tests for a simple "addition" function:
Funprog ADD(SUMMAND1, SUMMAND2)
Value Integer SUMMAND1
Value Integer SUMMAND2
End SUMMAND1 + SUMMAND2
In order to verify that the sum of 2 and 5 is indeed 7, we write:
Call CHECK_EQUAL(func ADD(2, 5), 7) From AXUNIT
or
Local Integer RESULT
RESULT = func ADD(2, 5)
Call CHECK_EQUAL(RESULT, 7) From AXUNIT

Subprograms to verify the expected result

The framework supports the following:

  • CHECK_EQUAL(GOT, EXPECT):

    Check if GOT equals to EXPECTED

  • CHECK_NOTEQUAL(GOT, EXPECT):

    Check if GOT not equals to EXPECTED

  • CHECK_TRUE(GOT):

    Check if GOT is true -

  • CHECK_FALSE(GOT):

    Check if GOT is false

Checking the contents of an instance

An additional function is available (from patch 6) to allow to dump the content of an instance. The function is the following:

Call LOG_CLASS(MYINSTANCE,INSTANCE_NAME,ERRORS) From AXUNIT

Where:

  • MY_INSTANCE is the instance pointer we want to dump
  • INSTANCE_NAME is the name of the instance pointer (it will appear in the log)
  • ERRORS is a flag that will trigger the display of all the errors associated to the classes or to their properties. When this happens, the error is displayed on a line following the element where the error is triggered, with two asterisks before it, and the corresponding level of gravity (INFO, WARNING, ERROR, FATAL).

When arrays of children instances are present, all the indexes are written in the log, preceded by a line that is written in this format:

NNN: [xxx,MMM]

Where NNN is the current index, xxx is the status of the line as given by ASTALIN (NEW, DEL, NEWDEL, UPD, NOTMOD), and MMM the value of AORDER.

An example of such a log is given here:
 Instance MYORDER of C_YORDER
  |     ** AWARNING This order is really big
  |    String(80) COMMENT : ""
  |    Datetime CREDATTIM : "2014-07-03T15:22:18Z"
  |    String(5) CREUSR : "MARTIN"
  |    String(3) CURRENCY : "EUR"
  |    String(15) CUSTOMER : "ZAO007"
  |     ** AERROR ZAO007 : Record does not exist
  |    String(5) FCY : "AO011"
  |    Collection LINES of C_YLINE (1..3)
  |     001: [NEW,001]   
  |    |    Decimal DISCOUNT : 0
  |    |    Integer DLVQTY : 0
  |    |    String(3) FLAG : ""
  |    |    String(20) ITEM : "BMS005"
  |    |    ShortInt NUMLIN : 1
  |    |    String(16) ORDNUM : "994159249"
  |    |    Decimal PRICE : 3.14
  |    |    Integer QTY : 1
  |    |    ShortInt SORTLIN : 1
  |    |    Decimal UNITPRICE : 3.14
  |     002: [DEL,002]   
  |    |    Decimal DISCOUNT : 25
  |    |    Integer DLVQTY : 0
  |    |    String(3) FLAG : ""
  |    |    String(20) ITEM : "BMS007"
  |    |    ShortInt NUMLIN : 2
  |    |    String(16) ORDNUM : "994159249"
  |    |    Decimal PRICE : 1500
  |    |    Integer QTY : 20
  |    |    ShortInt SORTLIN : 2
  |    |    Decimal UNITPRICE : 100
  |     003: [UPD,003]   
  |    |    Decimal DISCOUNT : 95
  |    |     ** AERROR Discount too high
  |    |    Integer DLVQTY : 0
  |    |    String(3) FLAG : ""
  |    |    String(20) ITEM : "BMS007"
  |    |    ShortInt NUMLIN : 3
  |    |    String(16) ORDNUM : "994159249"
  |    |    Decimal PRICE : -100
  |    |    Integer QTY : 20
  |    |    ShortInt SORTLIN : 3
  |    |    Decimal UNITPRICE : -100
  |    |     ** AERROR Price must be positive
  |    String(20) OPERATION : ""
  |    TinyInt ORDCLO : 1
  |    Date ORDDAT : "2014-07-14"
  |    String(16) ORDNUM : "994159249"
  |    TinyInt ORDSTA : 1
  |    Decimal TOTAL : 1403.14
  |    Datetime UPDDATTIM : "0000-00-00T00:00:00Z"
  |    String(5) UPDUSR : ""

Running a unit test

Different options are available to run a unit test.

Run an individual test in the Eclipse console

In the Eclipse console, type a func call with the full qualified name of the test suite. For example:

CODECODE CODEsh
=>func qlfar_encode.testsuite
QLFAR_ENCODE - REQ-70693 - 4 Succeed, 0 Failed, 80ms elapsed
Trace has been written to '/produits/v160/SOLSUPV6/dossiers/SUPERV/TRA/QLFAR_ENCODE_ERBOU.tra'
JSON result is available at: http://sodaix02/Adonix_SOLSUPV6/SUPERV/TMP/QLFAR_ENCODE_ERBOU.json

A summary of the result is generated with the number of successful and failed test cases and the elapsed time spent by the test session.

Run all tests

Running all tests could last a while. For this reason, it is recommended to not run them in the Eclipse console. To run all tests, call func AXUNIT.RUN_ALL.

Run some tests

It is possible to run all tests or exclude some of them by calling the func AXUNIT.RUN_ALL2(EXCLUDES, NODEBUG) function.

This function requires two arguments:

  1. EXCLUDES A string of program names separated by ;
  2. NODEBUG A value indicating if the debug mode must be disabled.
The following example calls all unit tests except `QLFAS_ASYRMAIN` while preventing the triggering of the debugger:
CODECODE CODEsh
func AXUNIT.RUN_ALL2(";QLFAS_ASYRMAIN;", 1)

Run a subset of unit tests

By using an appropriate name convention, it is simple to run a subset of unit tests by calling the func AXUNIT.RUN_SET("AS") function. This function will call all QLFAS_*.src unit tests (all supervisor tests).

Run in batch mode

This is the recommended way to run all tests. Create a batch task that calls func AXUNIT.RUN_ALL or func AXUNIT.RUN_ALL2("", 1).