Search code examples
objective-ccocoac-preprocessorocunit

Unit testing in presence of C API functions


My application uses third-party C APIs for some tasks. I was wondering how to unit test the classes that make calls to the API, since the unit testing environment is impractical to set up so that it works.

An example. One of my classes might have a method like this:

- (id) doSomeStuff
{
    // my code
    // ...
    thirdPartyDoStuffFunction(17, 4);
    // etc...
}

The corresponding unit test:

- (void) testDoSomeStuff
{
    MyClass *x = [[MyClass alloc] init];
    id result = [x doSomeStuff];

    STAssertNotNil(result, @"Result mustn't be nil");
}

Let's say that for reasons beyond my control, calling thirdPartyDoStuffFunction while running the unit testing target will crash the test. For this reason, and because I want to make sure the function has been called with the right arguments, I would like to mock it.

This is what I have tried so far:

  • Defining preprocessor macros that redirect the third-party functions to mock versions of them. However, so far I couldn't make that work. First, I have tried to define a preprocessor constant, UNIT_TEST in the unit test target's configuration, but the constant only has effect on my unit test classes, not on the classes I am testing.
  • I have then found this thread about creating a prefix header for the test target, which seems to have exactly the same effect as the first method: The UNIT_TEST constant defined in the prefix header is only visible in the test classes, not the tested classes.

Update: Both of the above approaches work for me now. The reason they didn't seems to be that the implementation files (.m) of the classes under test had not been added to the unit test target. Once I added them, the macros are being executed as expected, i.e. they override the third-party API.

I still wonder why there were no compiler/linker errors and the unit tests actually ran while some of the implementation wasn't present in their target, but the important thing is that it works now.


Solution

  • You should create a header file TestRedirect.h with the following contents:

    #ifdef UNIT_TESTING
    
    #define thirdPartyDoStuffFunction MyTest_thirdPartyDoStuffFunction
    ..........
    
    #endif
    

    You include this file into all files with your classes that you want to test. After that you create 2 different builds:

    • one with the compiler key -DUNIT_TESTING for testing;
    • other without that key for regular work.

    Your test functions should be defined simply as MyTest_thirdPartyDoStuffFunction and they should be included into the linking only in the test configuration.