Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 530
driis Avatar asked Sep 27 '12 09:09

driis


1 Answers

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?

like image 149
Erik Doernenburg Avatar answered Nov 15 '22 01:11

Erik Doernenburg