After writing source code, the same question always arises: Does this piece of code work as it is supposed to work?
Without any doubt, testing code is one of the most important and efficient means to detect bugs and to ensure that software still works after changing it. Therefore, a testing framework is integrated in Obix.
Before looking at how to write tests in Obix, let us recall that the Instruction executer presented in Chapter 2, First example: "Hello world!" is sometimes useful to quickly test code. For example, to test factory bank_customer we created in the previous chapter, enter the following code in the Instruction executer:
var bank_customer customer_giovanni = fa_bank_customer.co_create ( & identifier = 28 & name = "Giovanni Spiridigliotzky" & city = "Rome" ) console.message ( "Id : " & customer_giovanni.identifier.to_string ) console.message ( "name: " & customer_giovanni.name ) console.message ( "city: " & customer_giovanni.city )
Click Execute.
The system console displays:
Id : 28 name: Giovanni Spiridigliotzky city: Rome
One way of testing factory bank_account is to write a test script at the end of the factory's source code. The role of such a test script is to check the correct working of all features in the factory. This is done by using the features in different ways and then verifying the faultlessness with the verify instruction.
Here is a short example of a test script. The code should be self-explanatory. Enter it just before the end factory instruction, as shown below:
factory bank_account type:bank_account
...
test
script
// create a customer
var bank_customer bc = fa_bank_customer.create ( &
identifier = 10 &
name = "Foo" &
city = "Bar" )
// create an account
var bank_account account = create ( bc )
// check customer attribute of account
verify account.customer =r bc
verify account.customer.name =v "Foo"
// balance must be 0 after creation
verify account.balance =v 0
// add 100 to the account
account.pay_in ( 100 )
// verify balance
verify account.balance =v 100
// withdraw 70
account.withdraw ( 70 )
// verify balance
verify account.balance =v 30
// withdraw 10
account.withdraw ( 10 )
// verify balance
verify account.balance =v 20
// ...
end script
end test
end factoryAs we can see, the verify instruction is used again and again to verify that the factory behaves as it should behave. The verify keyword is simply followed by a boolean expression that must evaluate to yes for the test to pass.
How are test scripts executed? Whenever the compiler encounters at least one test script in a source code file, it automatically creates command co_test_ for the software component defined in that file. Calling co_test_ executes all test scripts in the source code. co_test_ has one input argument of type test_fail_list. This input argument is used to automatically save all test fails encountered during test execution.
To execute the above test script, enter the following code in se_explore:
service explore
command start
script
// create list to hold all test fails encountered during testing
var ty_test_fail_list v_test_fail_list = fa_test_fail_list.co_create
// execute all test scripts in fa_bank_account
fa_bank_account.co_test_ ( v_test_fail_list )
// display the result of testing
v_test_fail_list.co_display_result
end script
end command
end serviceNow compile and run the application. The following message will appear on the system console:
No test fails detected.
To see how test fails are displayed, let's replace
verify account.balance =v 100
with
verify account.balance =v 101
Compile and run again. The following test fail message displays:
Test fail list:
feature: fa_bank_account.test
library: li_explore.li_doc_examples.li_tutorial
line: 22
instruction: verify account.balance =v 101
message: A test did not pass validation. [test_fail]
1 test fail detected.There are several options for producing more explicit test fail messages:
It is often useful to display the expected value as well as the real value, instead of simply saying that the test failed. We can do this by inserting the keyword compare and replacing
verify account.balance =v 101
with
verify account.balance compare =v 101
We can provide a more explicit message instead of Obix's default message by defining a value for property error_message.
It is also possible to assign an identifier to each test case, through property error_id. This can be useful for example to classify different kinds of test fails for statistical evaluations.
If we want to provide more information about the cause of failure in a structured way, we can assign an object to the error_data property.
Replace
verify account.balance =v 100
with
verify account.balance compare =v 101 & error_message: "a_balance update error" & error_id: balance_update & error_data: "delta: " & (account.balance - 101).co_to_string
Compile and run again. Now the test fail message is:
Test fail list:
feature: fa_bank_account.test
library: li_explore.li_doc_examples.li_tutorial
line: 23
instruction: verify account.balance compare =v 101 &
error_message: "a_balance update error" &
error_id: balance_update &
error_data: "delta: " & (101 - account.balance).co_to_string
message: a_balance update error [balance_update]
data: delta: 1
left value: 100
right value: 101
1 test fail detected.Instead of simply displaying test fails on the system console (with instruction v_test_fail_list.co_display_result), it would of course also be possible to analyze and explore them programmatically, because the list of all test fails is accessible through variable test_fail_list.
There is more to say about testing, but this concludes our short introduction into testing in Obix, a useful capability that should help to avoid displaying embarrassing screens like the following one to customers:

For more information and further examples on testing see Chapter 21, Testing in the programming manual.