是否有一种惯用的Objective-C技术使init方法返回一个存根实现进行测试?

时间:2013-05-16 09:51:29

标签: objective-c unit-testing factory-pattern idioms ocmockito

我正在为一个Foo课程编写单元测试,该课程有一个合作者Bar。我想在Bar的测试中使用Foo的手动构建的存根实现。

如果我在Java中这样做,我会给Foo一个BarFactory合作者并在MockBarFactory的{​​{1}}测试中注入一个总是返回{{1} }}

我知道这种技术在Objective-C中运行良好,但它并没有让我觉得在动态语言中这是一个特别惯用的事情。我想知道我是否可以做任何棘手的事情,这会导致Foo在我运行单元测试时返回StubBar,但在“现实生活”中给我[[Bar alloc] init]的正常实现。

或者明显的工厂模式在这种情况下最适合使用?

2 个答案:

答案 0 :(得分:1)

您可以在init中返回其他对象。这就是为什么你应该将[super init]的返回值分配给self。尝试这样的事情:

@implementation Bar 

- (id)init {
    if (UNIT_TEST) {
        self = [[StubBar alloc] init];
        if (self) {
            // do unit test init here
        }
    } else {
        self = [super init];
        if (self) {
            // do regular init here
        }
    }
    return self;
}

...

@end

注意:这应该在ARC下工作。如果您不使用ARC,请确保在分配新的self实例之前发布StubBar

<小时/> 如果您想避免将与单元测试相关的代码编译到主目标中:

@implementation Bar 

- (id)init {
#if UNIT_TEST
    self = [[StubBar alloc] init];
    if (self) {
        // do unit test init here
    }
#else
    self = [super init];
    if (self) {
        // do regular init here
    }
#endif
    return self;
}

...

@end

如果您想完全分离单元测试和实际代码,您可以只有两个不同版本的Bar类。一个将使用真实代码编译,另一个将使用单元测试目标。


您可以轻松地分配实例,而无需在编译时知道确切的类类型,如下所示:

id someBar = [[someClass alloc] init]; // assuming someClass is of type Class

或:

id someBar = [[NSClassFromString(@"Bar") alloc] init];

第一个是优选的。你可以使用它来拥有一个默认的类类型,你可以在进行单元测试时改变它。作为Foo本身的属性,或者在单元测试时重新定义的预处理器宏。

答案 1 :(得分:0)

您正在寻找的是OCMock的内容。 它允许您从类,协议和特别是单元测试中构建模拟对象。