5

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?

Community
  • 1
  • 1
Brennan Vincent
  • 10,736
  • 9
  • 32
  • 54
  • 1
    No, there's no introspection (required for mocking) on C methods. Period. If you could do it, it would be extremely unreliable and undefined behavior. Maybe on a desktop app you could intersect the linking (like valgrind and strace/dtrace), but you just can't do that on the iPhone. – Kevin May 03 '14 at 02:23
  • 1
    With Obj-C methods, the actual implementation isn't chosen until the time at which that method is actually invoked at run time. With statically-linked C libraries (and I may be wrong on details here) the implementation of a function and where you call that in your code are bound at compile time. So it's really not possible – Carl Veazey May 03 '14 at 02:23

3 Answers3

4

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);

    //...
}
Mindaugas
  • 1,707
  • 13
  • 20
2

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.

Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
0

Another option would be to create a macro that renames the C function and calls your wrapper function in debug and the system C function in prod. This would allow you to mock/swizzle the Objective-C function for unit testing, but preserve the speed in prod.

There is one serious concern with this approach in that it changes your implementation between debug/prod, but it is a choice. In this case, I'd highly suggest having some integration tests and run them in a prod environment to confirm, to a pretty high degree of certainty, that the code is functioning as intended.

Anyway, if this interests you, please know/learn what you're doing as this can be a fairly dangerous approach if done incorrectly.

iamthearm
  • 513
  • 2
  • 15