OCMock:模拟协议未正确停止

Posted

技术标签:

【中文标题】OCMock:模拟协议未正确停止【英文标题】:OCMock: mocked protocol isn't stopped correctly 【发布时间】:2014-02-28 09:17:11 【问题描述】:

我有一个使用 OCMock 的测试用例,它执行以下操作:

CAAOAuth2AuthenticationManager *oAuth2AuthManager = [[CAAOAuth2AuthenticationManager alloc] init];
id authDelegate = [OCMockObject mockForProtocol:@protocol(CAAAuthenticationDelegate)];
id partialAuthManagerMock = [OCMockObject partialMockForObject:oAuth2AuthManager];
id resultMock = [OCMockObject mockForClass:[CAAOAuth2AuthenticationResult class]];
[[authDelegate reject] didFailWithError:OCMOCK_ANY];

[[[partialAuthManagerMock expect] andForwardToRealObject] authenticateWithResult:OCMOCK_ANY formData:OCMOCK_ANY delegate:authDelegate];
[[partialAuthManagerMock reject] authenticateWithOptions:OCMOCK_ANY delegate:authDelegate];

[[[resultMock expect] andReturnValue:OCMOCK_VALUE(YES) ] isAuthenticated];
[[resultMock reject] refreshToken];

当我运行测试用例时,同样使用 CAAAuthenticationDelegate 协议的第二个测试用例(完全不同的测试类和文件)因 SIGABRT 失败:

2014-02-28 10:11:24.594 otest[37161:303] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'OCMockObject[CAAAuthenticationDelegate]: unexpected method invoked: didReceiveAuthenticationWithResult:OCMockObject[CAAOAuth2AuthenticationResult] 
stubbed:    didFailWithError:<OCMAnyConstraint: 0xa913fc0>'

但是,我在第二个测试用例中没有使用任何模拟。我试图用stopMocking 清除模拟但没有成功。

以下模拟设置没有任何问题:

[[authDelegate reject] didFailWithError:OCMOCK_ANY];

[[[partialAuthManagerMock expect] andForwardToRealObject] authenticateWithResult:OCMOCK_ANY formData:OCMOCK_ANY delegate:authDelegate];
[[partialAuthManagerMock expect] authenticateWithOptions:OCMOCK_ANY delegate:authDelegate];

[[[resultMock expect] andReturnValue:OCMOCK_VALUE(NO) ] isAuthenticated];
[[[resultMock expect] andReturn:refreshToken] refreshToken];

谁能告诉我,为什么会这样?

【问题讨论】:

抛出异常时完整的堆栈跟踪是什么?你在某个地方留下了一个在记忆中晃来晃去的模拟。很可能是你的 andForwardToRealObject,这是在做任何类型的异步活动,测试完成后可能会在内存中徘徊吗? 【参考方案1】:

作为一种解决方法,您能否创建一个协议的空实现,然后模拟一个真实对象?我用这种方法运气更好——嘲笑协议只会让我感到不安。

@interface TestAuthDelegateImpl : NSObject <CAAAuthenticationDelegate>
@end
@implementation
- (void)didFailWithError:(id)whatever;
@end

类似的东西。然后只是 mockForClass 它 - 可能会更好。

【讨论】:

【参考方案2】:

这似乎意味着您的 CAAOAuth2AuthenticationManager 实例在以后的测试中仍然存在,并且仍然在其上设置了旧的模拟委托,并且在其上调用了一些方法,从而导致调用了该委托方法。 CAAOAuth2AuthenticationManager 是单例类型的对象,还是第二个测试中使用的同一实例?完成后,我会在第一次测试中将授权管理器上的委托重置为 nil。

您也可以使用 niceMockForProtocol,它会默默地忽略任何没有明确拒绝设置的方法调用。通过异常,听起来拒绝已被删除,并且委托模拟现在只会对发送给它的任何方法抛出异常,因为也没有设置任何期望。

此外,我会在实际调用您的真实代码时使用 STAssertNoThrow()(这可能发生在您在上面显示的设置之后)。拒绝和意外方法将引发异常,这可能导致模拟对象无法正确释放并为后续测试带来问题。如果有问题的测试通过了,这可能不是问题。

最后要检查的是您的委托属性是否声明为“assign”而不是“weak”。如果它是“分配”,并且您没有将其归零并且它被释放,那么任何事情都可能发生(段错误,或者在同一内存地址分配了一个全新的对象)。不过,这似乎也不太可能。

【讨论】:

以上是关于OCMock:模拟协议未正确停止的主要内容,如果未能解决你的问题,请参考以下文章

xcode 6.1 未加载正确的 iPhone 6 和 6+ 模拟器

使用 OCMock 模拟块期望

OCMock - 试图模拟 NSEntityDescription

Worklight 6.2 - 移动浏览器模拟器未正确呈现应用程序

CAGradientLayer 框架未正确显示

OCMock 模拟 UIImagePickerController