我目前正在创建一个 TDD 应用程序,但在测试我的应用程序所需的 C 函数的使用时遇到问题。这是我需要测试的示例函数:
UIImageWriteToSavedPhotosAlbum(imageToBeSaved, nil, nil, nil)
如何模拟或存根 TDD 的 C 方法?
我目前正在创建一个 TDD 应用程序,但在测试我的应用程序所需的 C 函数的使用时遇到问题。这是我需要测试的示例函数:
UIImageWriteToSavedPhotosAlbum(imageToBeSaved, nil, nil, nil)
如何模拟或存根 TDD 的 C 方法?
有几种方法可以做到这一点,但不如 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。
目标 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)];
我希望这有帮助 :)
我喜欢将 C 函数包装在 Objective 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