我想知道如何测试这个。我有一个方法,它接受一个参数,并根据该参数的一些属性创建另一个对象并对其进行操作。代码看起来像这样:
- (void) navigate:(NavContext *)context {
Destination * dest = [[Destination alloc] initWithContext:context];
if (context.isValid) {
[dest doSomething];
} else {
// something else
}
[dest release];
}
我要验证的是,如果context.isValid为true,那么在dest上调用doSomething,但我不知道如何使用OCMock或任何其他传统测试方法测试它(或者甚至可能)该对象完全在方法范围内创建。我这是错误的方式吗?
答案 0 :(得分:5)
您可以使用OCMock,但是您必须修改代码以获取Destination
对象或使用可以首先替换为模拟对象的单例对象。
最简洁的方法可能是实现
-(void) navigate:(NavContext *)context destination:(Destination *)dest;
方法。将-(void) navigate:(NavContext *)context
的实施更改为以下内容:
- (void) navigate:(NavContext *)context {
Destination * dest = [[Destination alloc] initWithContext:context];
[self navigate:context destination:dest];
[dest release];
}
这将允许您的测试直接使用额外参数调用该方法。 (在其他语言中,您只需为destination参数提供默认值即可实现此目的,但Objective-C不支持默认参数。)
答案 1 :(得分:1)
我要验证的是,如果context.isValid为true,则在dest上调用doSomething
我认为你可能在这里测试错误的东西。您可以安全地假设(我希望)布尔语句在ObjC中正常工作。您不想要测试Context对象吗?如果context.isValid,则保证[dest doSomething]分支被执行。
答案 2 :(得分:0)
使用像method swizzling这样有趣的技术是完全可能的,但它可能以错误的方式进行。如果完全没有办法观察从单元测试中调用doSomething
的效果,那么它是否会调用doSomething
实现细节?
(如果您要进行此测试,实现目标的一种方法是将doSomething
Destination
方法替换为通知您的单元测试的方法,然后将调用传递给{{ 1}}。)
答案 3 :(得分:0)
我喜欢在这种情况下使用工厂方法。
@interface Destination(Factory)
+ (Destination *)destinationWithContext:(NavContext *)context;
@end
@implementation Destination(Factory)
+ (Destination *)destinationWithContext:(NavContext *)context
{
return [[Destination alloc] initWithContext:context];
}
@end
然后我做了一个FakeClass:
#import "Destination+Factory.h"
@interface FakeDestination : Destination
+ (id)sharedInstance;
+ (void)setSharedInstance:(id)sharedInstance;
// Note! Instance method!
- (Destination *)destinationWithContext:(NavContext *)context;
@end
@implementation FakeDestination
+ (id)sharedInstance
{
static id _sharedInstance = nil;
if (!_sharedInstance)
{
_sharedInstance = [[FakeDestination alloc] init];
}
return _sharedInstance;
}
+ (void)setSharedInstance:(id)sharedInstance
{
_sharedInstance = sharedInstance;
}
// Overrides
+ (Destination *)destinationWithContext:(NavContext *)context { [FakeDestination.sharedInstance destinationWithContext:context]; }
// Instance
- (Destination *)destinationWithContext:(NavContext *)context { return nil; }
@end
完成此设置后,您只需+ (Destination *)destinationWithContext:(NavContext *)context;
现在你已经设置为:
id destinationMock = [OCMock mockForClass:FakeDestination.class];
// do the swizzle
[FakeDestination setSharedInstance:destinationMock];
[[destinationMock expect] doSomething];
// Call your method
[destinationMock verify];
这是预先编写的大量编码,但它非常可重复使用。