Testing

Actually this tutorial is organized in the wrong order. It should not have started with how to make objects but with how to test them. Before you write some code, you must write a test that verifies the code. So for every piece of code you have an automated test that verifies its correctness.

This is even more important in OBC programming. Since OBC objects do not have a type, the compiler will not help you in detecting errors. It is your obligation to write your error testing software. This way you can also test a lot more, than a compiler ever could.

test.h and test.c offer a simple testing framework. Here is an example how such a test looks:

carT.c

    #include "../obc/test.h"
    #include "car.h"
    
    static void accelerate() {
        Car car = car_create();
    
        testEqual(0, car_getSpeed(car));
        car_accelerate(car);
        test(0 < car_getSpeed(car), "car did not accelerate");
        car_destroy(car);
    }
    
    
    Test car_test() {
        Test t = test_create("Car", test_init, test_clean);
        test_add(t, accelerate);
        return t;
    }
  

main.c

    ...
    #include "../obc/test.h"
    
    Test car_test();
    Test boat_test();
    
    Test tests() {
        Test t = test_create("OBC demo", NULL, NULL);
        test_addTest(t, car_test() );
        test_addTest(t, boat_test() );
        return t;
    }
    
    int main(){
        test_run(tests, 2); 
    
        ...
    }
  

Define functions that test an aspect of an object (ex. accelerate() ). Use the macro test(condition, comment) to do so. Add the functions to a unit test that tests the whole object functionality (ex. car_test() ). Add all unit tests to a main test unit ( tests() ). Run this tests at the BEGINNING of main with test_run(tests, 2); . This way your objects are tested after each modification.

The test macros can also be used without tests. Then they behave like normal asserts. The are turned off when NDEBUG is defined. More test macros are:

For a description of all possibilities look into test.h .Also have a look at the real carT.c file for a more sophisticated example. There can you also find an implementation for boatT.c .

To test that an object properly implements an interface, an interface test is needed. Interface tests need to be applied to different implementations. So you must parametrise it with the object constructor that should be tested to implement the interface.
Use test_addObjConstructor(test, type, constructor) to inject a constructor into a test unit. Inside test functions test_createObj(type) will then create an object to test. Here is how the vehicle interface test might look and how it is used to verify the vehicle implementation of car:

vehicleT.c

    #include "../obc/test.h"
    #include "../obc/objI.h"
    #include "vehicleI.h"
    
    static void accelerate() {
        Obj vehicle = test_createObj("Vehicle");
    
        vehicle_accelerate(vehicle);
        test(0 < vehicle_getSpeed(vehicle), "vehicle did not accelerate");
        obj_destroy(vehicle);
    }
    
    Test vehicle_test(const char* name,  TestObjConstructor constructor ) {
        Test t = test_create(name, NULL, NULL);
        test_addObjConstructor(t, "Vehicle", constructor);
        test_add(t, accelerate);
        return t;
    }
  

carT.c

  
  ...
  
    static Test car_testBase() {
        Test t = test_create("Car base", NULL, NULL);
        test_add(t, creation);
        test_add(t, accelerate);
        test_add(t, brake);
        return t;
    }
    
    Test car_test() {
        Test t = test_create("Car", NULL, NULL);
        test_addTest(t, car_testBase());
        test_addTest(t, vehicle_test("Car vehicle", (TestObjConstructor)car_create));
        return t;
    }
  

Note, that a test can not hold test functions and sub tests at the same time. For this reason a car sub test "Car base" has been introduced to hold the function tests for the car.