我目前正在创建一个TDD应用程序,但是我在测试应用程序所需的C函数的使用时遇到了问题。这是我需要测试的示例函数:
UIImageWriteToSavedPhotosAlbum(imageToBeSaved, nil, nil, nil)
如何为TDD模拟或存根C方法?
答案 0 :(得分:4)
有几种方法可以做到但不如OOP语言那么漂亮。这是我使用的列表:
这种方法是我最喜欢的方法,因为在我看来这是最灵活的。
int (*_fun) (int);
int fun (int a);
int fun_mock (int a);
_fun = fun_mock;
_fun = fun;
...
_fun ();
...
如果要制作TDD,则需要编译测试文件和主文件(或其余文件)。否则不要编译测试文件。
在标题文件(* .h)
中如果你想打个招呼
#define FUN fun
int fun (int a);
int fun_mock (int a);
在标题文件(* .h)
中如果你想调用模拟版
#define FUN fun_mock
int fun (int a);
int fun_mock (int a);
调用函数(main.C)
...
FUN ();
...
使用此方法,我们需要在编译任何模块之前设置正确的定义。
说实话,我从未使用过这种方法,但我在其他地方读过。主要思想是为模块的所有不同函数提供结构指针,每当您想要更改函数时,只需更改该结构上此函数指针的地址即可。最后是与第一种方法类似的策略,但以不同的方式实现。
test-dept是一个相对较新的C单元测试框架,允许您对函数进行运行时存根。我发现它很容易使用 - 这是他们的文档中的一个例子:
void test_stringify_cannot_malloc_returns_sane_result() {
replace_function(&malloc, &always_failing_malloc);
char *h = stringify('h');
assert_string_equals("cannot_stringify", h);
}
虽然下载部分有点过时,但似乎相当积极地开发 - 作者修复了一个我非常及时的问题。你可以通过以下方式获得最新版本(我一直在使用,没有问题):
svn checkout http://test-dept.googlecode.com/svn/trunk/ test-dept-read-only 该版本于2011年10月更新。
但是,由于使用汇编程序实现了存根,因此可能需要一些努力才能使其支持ARM。
答案 1 :(得分:2)
我喜欢在Objective C方法中包装C函数,以便可以使用Objective C模拟框架(例如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
答案 2 :(得分:2)
目标C解决方案
定义一个返回C函数指针的类方法:
+ (void (*)(UIImage *, id, SEL, void *))writeToSavedPhotosAlbumFunction {
return &UIImageWriteToSavedPhotosAlbum;
}
在生产代码中使用此类方法返回的C函数:
[SomeClass writeToSavedPhotosAlbumFunction](myImage, myTarget, mySelector, myContextInfo);
定义一个伪C函数来替换我们依赖的C函数:
static void FakeWriteToSavedPhotosAlbum(UIImage *image, id completionTarget, SEL completionSelector, void *contextInfo) {
// Verify arguments.
// If this function has a return type, return a value.
// To verify that this function was called, write to a global variable
// then have the test case check the value of that global variable.
}
将类方法存根以在测试代码中返回指向伪C函数的指针(此示例使用Kiwi):
[SomeClass stub:@selector(writeToSavedPhotosAlbumFunction) andReturn:theValue(&FakeWriteToSavedPhotosAlbum)];
我希望这会有所帮助:)