7

我目前正在创建一个 TDD 应用程序,但在测试我的应用程序所需的 C 函数的使用时遇到问题。这是我需要测试的示例函数:

UIImageWriteToSavedPhotosAlbum(imageToBeSaved, nil, nil, nil)

如何模拟或存根 TDD 的 C 方法?

4

3 回答 3

4

有几种方法可以做到这一点,但不如 OOP 语言那么漂亮。这里列出我使用的:

使用函数指针

这种方法是我最喜欢的一种,因为在我看来是最灵活的。

在头文件 (*.h)

int (*_fun) (int);

int fun (int a);

int fun_mock (int a);

在测试用例文件 (*.C) 中

_fun = fun_mock;

在正常情况下文件 (*.C)

_fun = fun;

调用函数(main.C)

...
_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。

我从https://stackoverflow.com/a/9613821/2420872复制的最后一点。

于 2013-05-26T17:56:10.160 回答
3

目标 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)];

我希望这有帮助 :)

于 2014-05-08T23:45:11.233 回答
3

我喜欢将 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
于 2014-02-13T21:47:22.167 回答