Chapter 5. Does it work?

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 factory

As 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 service

Now 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:

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.