我一直在使用Objective C和Cocoa / iOS,并且像个模仿者一样进行测试。 (Definition)
在Objective C中有两种方法可以做到这一点我知道:
我应该使用哪种方式?
我不喜欢其中任何一个。但是我必须使用一个...除非有其他选择我不知道。
1。依赖注入
这会使我的代码变得混乱,特别是当SUT有2/3个合作者时。如果SUT需要传递1/2参数,事情看起来确实非常混乱。
我明白,除了3之外,还有太多的依赖关系,对象应该被分成其他部分......但即使有2个依赖关系和1个参数,它仍然像罪一样难看。
2。设置内部状态
这与课堂内部混淆 - 我认为这是测试中的一个大禁忌。
访问者肯定是出局的 - 他们会公开没有人应该知道的数据。 我可以使用setValue:forKey:...但这感觉就像一个可怕的黑客。
这也意味着我必须初始化SUT然后将真正的协作者替换为模拟协作者,然后运行测试中的方法,这感觉很乱。
我的问题
在做BDD时,在Objective C中模拟协作者最好的方法是什么?
代码
使用setValue进行模拟:forKey:
@interface JGCompositeCommand : JGCommand <JGCompositeCommandProtocol> {
NSMutableArray *commands;
JGCommandFinderFactory *commandFinderFactory;
}
-(id <JGCommandProtocol>)initWithName:(NSString *)name_ recoverer:(id <JGCommandRecoveryProtocol>)recoverer_ executor:(id <JGCommandExecutorProtocol>)executor_;
@end
@implementation JGCompositeCommand
-(id)initWithName:(NSString *)name_ recoverer:(id)recoverer_ {
self = [super initWithName:name_ recoverer:recoverer_];
if (self) {
commands = [NSMutableArray array];
commandFinderFactory = [[JGCommandFinderFactory alloc] init];
}
return self;
}
-(id <JGCommandProtocol>)commandWithName:(NSString *)name_ {
return [[commandFinderFactory commandFinderWithCommandName:name_ andCommands:commands] findCommandWithName];
}
@end
@interface JGCommandTestCase : SenTestCase {
JGCompositeCommand *compositeCommand;
OCMockObject *commandFinderFactoryMock;
}
@end
@implementation JGCommandTestCase
-(void)setUp {
[super setUp];
compositeCommand = [[JGCompositeCommand alloc] initWithName:@"" recoverer:nil];
commandFinderFactoryMock = [OCMockObject mockForClass:[JGCommandFinderFactory class]];
// Hack alert! Ugh.
[compositeCommand setValue:commandFinderFactoryMock forKey:@"commandFinderFactory"];
}
-(void)testGivenCommandNotFoundShouldThrow {
// ** Setup **
[[[commandFinderFactoryMock expect] andReturn:...] commandFinderWithCommandName:... andCommands:...];
// ** Execute **
[compositeCommand commandWithName:@"Blah"];
// ** Asserts **
[commandFinderFactoryMock verify];
}
@end
答案 0 :(得分:0)
我们采用的方法是让合作者单身,并提供一种在测试时注入模拟实例的方法:
static JGCommandFinder *sharedFinder = nil;
+(JGCommandFinder *)sharedFinder {
if (sharedFinder == nil) sharedFinder = [[JGCommandFinder alloc] init];
return sharedFinder;
}
+(void)setSharedFinder:(JGCommandFinder *)instance {
sharedFinder = instance;
}
它非常灵活,因为您可以使用实际对象,即使真实对象已经初始化也会注入模拟,并通过将实例设置为nil来重置它以使用真实对象。