在我的夹具setUp中我有以下
-(void)setUp{
_vc = [OCMockObject partialMockForObject:[[InboxViewController alloc] init]];
//stub out the view stuff
[[_vc stub] removeTask:OCMOCK_ANY];
[[_vc stub] insertTask:OCMOCK_ANY];
}
夹具中有15个测试,但是,我需要实际测试这两个方法是否被调用,所以我写了2个测试
-(void)someTest{
[[_vc expect] removeTask:OCMOCK_ANY];
[_vc removeAllTasksFromList:taskList notInList:newTaskList];
[_vc verify];
}
但该测试失败
我也试过
-(void)someTest{
[[_vc stopMocking];
[[_vc expect] removeTask:OCMOCK_ANY];
[[_vc stub] removeTask:OCMOCK_ANY];
[_vc removeAllTasksFromList:taskList notInList:newTaskList];
[_vc verify];
}
但测试仍然失败。我错过了什么,或者这只是OCMock的工作原理?
我能使它发挥作用的唯一方法就是这样
-(void)someTest{
//re create and init the mock object
_vc = [OCMockObject partialMockForObject:[[InboxViewController alloc] init]];
[[_vc expect] removeTask:OCMOCK_ANY];
[[_vc stub] removeTask:OCMOCK_ANY];
[_vc removeAllTasksFromList:taskList notInList:newTaskList];
[_vc verify];
}
答案 0 :(得分:8)
也许文档应该更清晰。 stopMocking 对部分模拟的作用是将真实对象(在您的情况下为InboxViewController)恢复到其原始状态。调用 stopMocking 不会重置模拟对象,这意味着它不会清除存根和期望。您始终可以调用 stopMocking ,然后为同一个真实对象创建一个新模拟。
正如在另一个答案中指出的那样,通常可以更好地避免使用存根和期望相同的方法,但是如果必须这样做,请确保在存根之前设置期望值;否则存根将处理调用,期望永远不会看到它们。
我知道传统上很多人建议使用设置方法来设置测试主题。多年来,我个人的经验是,它通常不值得。在每个测试中保存几行可能看起来很有吸引力,但最终它确实在各个测试之间创建了耦合,使得套件更加脆弱。
答案 1 :(得分:2)
似乎您需要创建一个新模拟以期望已经存根的方法。 我建议你重新考虑在所有测试用例中使用部分模拟,如果你愿意,请提取:
[[_vc stub] removeTask:OCMOCK_ANY];
[[_vc stub] insertTask:OCMOCK_ANY];
进入帮助器方法并从您真正需要的测试中调用该方法,将其从setUp方法中删除。
而且,小提示:),您应该在[super setUp]
实施开始时致电setUp
。
答案 2 :(得分:2)
您可以执行以下操作从OCMockObject中删除存根,这样您就可以将stub
代码保留在-(void)setUp
中,但仍允许您在expect
中添加@interface OCMockObject (Custom)
- (void)removeStubWithName:(NSString*)stubName;
@end
@implementation OCMockObject (Custom)
- (void)removeStubWithName:(NSString*)stubName {
NSMutableArray* recordersToRemove = [NSMutableArray array];
for (id rec in recorders) {
NSRange range = [[rec description] rangeOfString:stubName];
if (NSNotFound == range.location) continue;
[recordersToRemove addObject:rec];
}
[recorders removeObjectsInArray:recordersToRemove];
}
@end
后来的测试。
将以下类别添加到测试中以返回OCMockObject ivar。
-(void)someTest{
[_vc removeStubWithName:@"removeTask"];
[[_vc expect] removeTask:OCMOCK_ANY];
[_vc removeAllTasksFromList:taskList notInList:newTaskList];
[_vc verify];
}
用法示例: 在添加期望之前,请确保删除方法的存根。
{{1}}
这将允许您的测试按预期运行。
这是一个有用的黑客攻击,至少在OCMock开发人员允许此功能之前。
答案 3 :(得分:1)
有几点需要注意:
我假设你在setUp
中存根,因为你有一些方法可能会或可能不会在测试中被调用。如果是这样,您可以像这样构建测试:
static InboxViewController *_vc;
static id mockInbox;
-(void)setUp{
_vc = [[InboxViewController alloc] init];
mockInbox = [OCMockObject partialMockForObject:_vc];
//stub out the view stuff
[[mockInbox stub] removeTask:OCMOCK_ANY];
[[mockInbox stub] insertTask:OCMOCK_ANY];
}
-(void)someTest{
[[mockInbox expect] somethingIExpectForThisTest:OCMOCK_ANY];
[_vc removeAllTasksFromList:taskList notInList:newTaskList];
[mockInbox verify];
}
-(void)someOtherTest{
[[mockInbox expect] someOtherThingIExpectForThisTest:OCMOCK_ANY];
[_vc doSomethingElse];
[mockInbox verify];
}
答案 4 :(得分:0)
给出存根和期望调用的顺序对于部分模拟很重要。在预期之前对方法进行存根将意味着发送到存根方法的任何消息都将永远不会满足期望。
另一方面,stopMocking只意味着部分模拟将停止与真实对象相关联。模拟仍然保留其记录器(存根和期望)并照常工作。您可以通过向您的真实对象发送removeAllTasksFromList:notInList
来验证这一点,在这种情况下,该对象不会分配给任何变量。在这种情况下,您将看到消息确实到达了对象的实现。但是,模拟仍将无法通过验证调用。通常,您应该在真实对象上调用方法。部分模拟仍将拦截消息。
正如在另一个答案中提到的,最好的方法是创建一个新的局部模拟并在存根方法之前调用expect。你甚至可以实现一个帮手:
- (void) setUpPartialMockWithExpect:(BOOL)needExpect {
_vc = [OCMockObject partialMockForObject:[[InboxViewController alloc] init]];
if( needExpect )
[[_vc expect] removeTask:OCMOCK_ANY];
//stub out the view stuff
[[_vc stub] removeTask:OCMOCK_ANY];
[[_vc stub] insertTask:OCMOCK_ANY];
}
并在每个-(void)test...