I'm currently creating an application that is TDD but I'm having problems testing the usage of C functions that are required for my application. Here is an example function that that I need to test:
UIImageWriteToSavedPhotosAlbum(imageToBeSaved, nil, nil, nil)
How can I mock or stub a C method for TDD?
There are several approaches to do it but is not as beautiful as in OOP languages. Here list which I use:
This approach is my favourite one because is the most flexible in my opinion.
int (*_fun) (int);
int fun (int a);
int fun_mock (int a);
_fun = fun_mock;
_fun = fun;
...
_fun ();
...
If you want to make the TDD, you need to compile the testing file and the main (or the rest of the files) one. Otherwise do not compile the testing file.
In the header file (*.h)
If you want to call fun
#define FUN fun
int fun (int a);
int fun_mock (int a);
In the header file (*.h)
If you want to call the mock version
#define FUN fun_mock
int fun (int a);
int fun_mock (int a);
Calling the function (main.C)
...
FUN ();
...
Using this method we need to set the correct define before we compile any of the modules.
To be honest I never used this method but I read in some others places. The main idea is to have a struct pointers for all the different functions of the module, and whenever you want to change a function you just change the address of this function pointer on that struct. In the end is a similar strategy that the first method but implemented in a different way.
test-dept is a relatively recent C unit testing framework that allows you to do runtime stubbing of functions. I found it very easy to use - here's an example from their docs:
void test_stringify_cannot_malloc_returns_sane_result() {
replace_function(&malloc, &always_failing_malloc);
char *h = stringify('h');
assert_string_equals("cannot_stringify", h);
}
Although the downloads section is a little out of date, it seems fairly actively developed - the author fixed an issue I had very promptly. You can get the latest version (which I've been using without issues) with:
svn checkout http://test-dept.googlecode.com/svn/trunk/ test-dept-read-only the version there was last updated in Oct 2011.
However, since the stubbing is achieved using assembler, it may need some effort to get it to support ARM.
The last point I copied from https://stackoverflow.com/a/9613821/2420872.
I like to wrap the C function in an Objective C method so that it can be stubbed/mocked/expected with an Objective C mocking framework (e.g. OCMock).
SomeClass.m:
@implementation SomeClass
#pragma mark - Public
// This is the method we will test. It calls a C function internally, which we want to stub.
- (void)someMethod {
UIImage *image = [self getImage];
[self writeImageToSavedPhotosAlbum:image
completionTarget:nil
completionSelector:nil
contextInfo:NULL];
}
#pragma mark - Private
- (void)writeImageToSavedPhotosAlbum:(UIImage *)image
completionTarget:(id)completionTarget
completionSelector:(SEL)completionSelector
contextInfo:(void *)contextInfo
{
UIImageWriteToSavedPhotosAlbum(image, completionTarget, completionSelector, contextInfo);
}
@end
SomeClassTests.m:
@interface SomeClass ()
- (void)writeImageToSavedPhotosAlbum:(UIImage *)image
completionTarget:(id)completionTarget
completionSelector:(SEL)completionSelector
contextInfo:(void *)contextInfo;
@end
@interface SomeClassTests : XCTestCase
@end
@implementation SomeClassTests
- (void)testSomeMethod {
SomeClass *someInstance = [[SomeClass alloc] init];
id someInstanceMock = [OCMockObject partialMockForObject:someInstance];
[[someInstanceMock stub] writeImageToSavedPhotosAlbum:[OCMArg any]
completionTarget:nil
completionSelector:nil
contextInfo:NULL];
[someInstance someMethod];
}
@end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With