0

I have a test case using OCMock which does the following:

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];

When I run the test cases, a second test case (completely different test class and file) which also uses the CAAAuthenticationDelegate protocol fails with 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>'

But, I don't use any mocks in the second test case. I was trying to clear the mocks with stopMocking with no success.

The following mock setup works without any problems:

[[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];

Can someone tell me, why this happens?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Denis Loh
  • 2,194
  • 2
  • 24
  • 38
  • 1
    What is the complete stack trace when the exception is thrown? You have left a mock dangling in memory somewhere. It is likely your andForwardToRealObject, is this doing any kind of asynchronous activity that could hang around in memory after the test completes? – ImHuntingWabbits Feb 28 '14 at 17:32

2 Answers2

0

As a workaround, can you create an empty implementation of the protocol and then mock a real object? I've had better luck with that method -- mocking protocols has only led to wonkiness for me.

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

Something like that. Then just mockForClass it -- might be better behaved.

Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
0

That seems to mean that your CAAOAuth2AuthenticationManager instance was still around in a later test, and still had the old mock delegate set on it, and some method was called on it which caused that delegate method to be called. Is CAAOAuth2AuthenticationManager a singleton-type object, or is the same instance used in the second test? I would reset the delegate to nil on the auth manager in the first test when it is done.

You can also use niceMockForProtocol, which will silently ignore any method calls which do not have an explicit reject set up on them. By the exception, it sounds like the reject has been removed, and the delegate mock will now just throw exceptions on any method sent to it, since there are also no expects set up.

Also, I would use STAssertNoThrow() around the actual call to your real code (which presumably happens after the setup you show above). Rejects and unexpected methods will raise exceptions, which can cause the mock objects to not get deallocated properly and create issues for subsequent tests. If the test in question passed though, that is probably not an issue here.

The last thing to check is if your delegate property is declared as "assign" instead of "weak". If it's "assign", and you don't nil it out and it gets freed, then anything could happen (segfault, or an entirely new object getting allocated at that same memory address). That also seems unlikely here though.

Carl Lindberg
  • 2,902
  • 18
  • 22