Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking C library functions in Objective-C

I'm a beginner to Objective-C, but I've used jUnit a fair bit.

For unit testing my code, I need to be able to mock the network, which I'm using through the CFStreamCreatePairWithSocketToHost function.

Per the answer to How to mock a C-function using OCMock, this is impossible in OCMock. Are there any frameworks or techniques that allow this?

I'd rather not commit to using some overcomplicated Java-esque IoC framework just to make my code easier to test, but I must say that one of the benefits of Spring is that issues like this rarely come up when you use everything through an interface and configure the implementations through dependency injection. Is that the way forward here, or is there a simpler solution?

like image 699
Brennan Vincent Avatar asked May 03 '14 01:05

Brennan Vincent


2 Answers

You can rebind system(all) function pointers in memory for testing. There are multiple techniques to do it(e.g. DYLD_INSERT_LIBRARIES), but the easiest way is to use convenience library from Facebook: https://github.com/facebook/fishhook

#import "fishhook.h"

static void *(*orig_malloc)(size_t __size);
void *my_malloc(size_t size)
{
    printf("Allocated: %zu\n", size);
    return orig_malloc(size);
}
 /* .. */
- (void)testMyTestCase
{
    //hook malloc
    rebind_symbols((struct rebinding[1]){{"malloc", my_malloc, (void *)&orig_malloc}}, 1);

    char *a = malloc(100);

    //restore original
    rebind_symbols((struct rebinding[1]){{"malloc", orig_malloc, NULL}}, 1);

    //...
}
like image 154
Mindaugas Avatar answered Nov 18 '22 13:11

Mindaugas


I don't know of any way to mock a C function. If it can be done, it would have to be a feature in the C compiler you're using.

As far as I know, Clang does not have any such feature, so you can't do it.

The CFStreamCreatePairWithSocketToHost() function is at a fixed location in memory and it cannot be moved nor can anything else be placed at the memory location - it's a system function shared by every app running on the device, changing it would break every other app that is currently executing.

The inability to do things like this with C function is the reason why C executes faster than most other programming languages. If you want to be able to mock functions, then do not write your code in the C language. Use objective-c instead, make a class MyCFStream with a method createPairWithSocketToHost, with a single line of code calling the C function use that everywhere in your app. You can easily mock the method in the class.

Your wrapper function will, however, be much slower than using the built in C function directly.

like image 2
Abhi Beckert Avatar answered Nov 18 '22 14:11

Abhi Beckert