8
votes

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?

1
Are you building OCMock from source? It would be interesting to put a breakpoint in the methodSignatureForSelector: method of OCProtocolMockObject. - Carl Veazey
Not building from source, just downloaded the static library. - driis
Can you post the actual protocol declaration? - Christopher Pickslay
Yes, just edited the question to include the full, actual protocol being used. - driis

1 Answers

2
votes

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?