OCMock, why can't I expect method on a protocol?
Asked Answered
K

1

8

Consider this code, which works (the loginWithEmail method gets expected as, well, expected):

_authenticationService = [[OCMockObject mockForClass:[AuthenticationService class]] retain];
[[_authenticationService expect] loginWithEmail:[OCMArg any] andPassword:[OCMArg any]];

Versus this code:

_authenticationService = [[OCMockObject mockForProtocol:@protocol(AuthenticationServiceProtocol)] retain];
[[_authenticationService expect] loginWithEmail:[OCMArg any] andPassword:[OCMArg any]];

The second code example fails on line 2 with the following error:

*** -[NSProxy doesNotRecognizeSelector:loginWithEmail:andPassword:] called! Unknown.m:0: error: -[MigratorTest methodRedacted] : ***
-[NSProxy doesNotRecognizeSelector:loginWithEmail:andPassword:] called!

AuthenticationServiceProtocol declares the method:

@protocol AuthenticationServiceProtocol <NSObject>
@property (nonatomic, retain) id<AuthenticationDelegate> authenticationDelegate;

- (void)loginWithEmail:(NSString *)email andPassword:(NSString *)password;
- (void)logout;
- (void)refreshToken;

@end

And it is implemented in the class:

@interface AuthenticationService : NSObject <AuthenticationServiceProtocol>

This is using OCMock for iOS.

Why does expectfail when the mock is a mockForProtocol?

Kellar answered 27/9, 2012 at 9:34 Comment(4)
Are you building OCMock from source? It would be interesting to put a breakpoint in the methodSignatureForSelector: method of OCProtocolMockObject.Poetess
Not building from source, just downloaded the static library.Kellar
Can you post the actual protocol declaration?Fiveandten
Yes, just edited the question to include the full, actual protocol being used.Kellar
S
2

This is curious. I have added the following class into the iOS5 example project:

@protocol AuthenticationServiceProtocol

- (void)loginWithEmail:(NSString *)email andPassword:(NSString *)password;

@end

@interface Foo : NSObject
{
    id<AuthenticationServiceProtocol> authService;
}

- (id)initWithAuthenticationService:(id<AuthenticationServiceProtocol>)anAuthService;
- (void)doStuff;

@end

@implementation Foo

- (id)initWithAuthenticationService:(id<AuthenticationServiceProtocol>)anAuthService
{
    self = [super init];
    authService = anAuthService;
    return self;
}

- (void)doStuff
{
    [authService loginWithEmail:@"x" andPassword:@"y"];
}

@end

@implementation ProtocolTests

- (void)testTheProtocol
{
    id authService = [OCMockObject mockForProtocol:@protocol(AuthenticationServiceProtocol)];
    id foo = [[Foo alloc] initWithAuthenticationService:authService];

    [[authService expect] loginWithEmail:[OCMArg any] andPassword:[OCMArg any]];

    [foo doStuff];

    [authService verify];
}

@end

When I run this in Xcode Version 4.5 (4G182) against the iPhone 6.0 Simulator the test passes. Is there any difference in how the mock object is used? In your case, where does _authenticationService get passed to? What is the recipient doing to it?

Schermerhorn answered 27/9, 2012 at 21:49 Comment(5)
Hi Erik, thanks for your answer. I have tried to reproduce this in a separate empty test project, and I can't, so I will have to investigate exactly what triggers the behaviour. I will get back to you if I find anything. FYI, the error occurs on the expect call, before the mock is passed to any of my code under test.Kellar
@Kellar I tried to reproduce it as well but couldn't. Which version of OCMock are you using?Wes
@Erik How can we get the argument passed during delegate method call?? Like in above case, doStuff method is calling authService method with arguments @"x" and @"y". How can I read @"x" and @"y" in test case?Refrigerator
@iVishal Unrelated. Please open a new question.Schermerhorn
I have the same issue, and I also can't reproduce this outside of my project. I opened github.com/erikdoe/ocmock/issues/78, but it's not very useful.Brash

© 2022 - 2024 — McMap. All rights reserved.