OMGPOP
OMGPOP

Reputation: 1004

OCMock unit test error

I use OCMock to test out singleton methods. I get "no such method exists in the mocked class." error for testSingleton method and infinite loop (the screenshot, the spinning indicator) for testSingletonWithBlock method

EDIT: download sample project here

https://drive.google.com/file/d/0B-iP0P7UfFj0LVFpWWpPb3RDZFU/edit?usp=sharing

enter image description here

Here is my implementation

manager:

@implementation Manager
+ (Manager *)sharedManager {
    static Manager *instance;
    dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        instance = [Manager new];
    });
    return instance;
}
- (int)getOne {
    return 1;
}
- (void)success:(BOOL)success completion:(void(^)(void))completion failure:(void(^)(void))failure {
    success ? completion() : failure();
}

view controller:

- (void)manager_printOne {
    int num = [[Manager sharedManager] getOne];
    NSLog(@"number is: %d", num);
}

- (void)manager_success:(BOOL)success completion:(void(^)(void))completion failure:(void(^)(void))failure {

    [[Manager sharedManager] success:success completion:completion failure:failure];

}

test view controller:

@interface coreDataTestTests : XCTestCase

@property (nonatomic, strong) id mockManager;

@property (nonatomic, strong) ViewController *viewController;

@end

@implementation coreDataTestTests

- (void)setUp
{
    [super setUp];

    self.viewController = [ViewController new];

    //for singleton
    self.mockManager = [Manager createNiceMockManager];
}

- (void)tearDown
{
    [super tearDown];

    self.viewController = nil;


    //Note: singleton need both, retain counts = 2
    self.mockManager = nil;
    [Manager releaseInstance];

}



- (void)testSingleton {

    NSLog(@"testSingleton");
    OCMStub([self.mockManager getOne]).andReturn(2);
    [self.viewController manager_printOne];

}



- (void)testSingletonWithBlock {

    NSLog(@"testSingletonWithBlock");

    [[[[self.mockHelper stub] ignoringNonObjectArgs] andDo:^(NSInvocation *invocation) {

        void(^block)(void);

        [invocation getArgument:&block atIndex:3];
        block();

    }] success:0 completion:[OCMArg any] failure:[OCMArg any]];


    [self.viewController manager_success:NO completion:^{
        NSLog(@"completion");
    } failure:^{
        NSLog(@"failure");
    }];



}




@end

manager category for unit test:

static Manager *mockManager = nil;

@implementation Manager

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

+ (Manager *)sharedManager {
    if (mockManager) {
        return mockManager;
    }
    return invokeSupersequentNoParameters();
}



#pragma clang diagnostic pop

+(id)createMockManager {
    mockManager = [OCMockObject mockForClass:[Manager class]];
    return mockManager;
}

+(id)createNiceMockManager {
    mockManager = [OCMockObject niceMockForClass:[Manager class]];
    return mockManager;
}

+(void)releaseInstance {
    mockManager = nil;
}

Upvotes: 1

Views: 695

Answers (2)

Erik Doernenburg
Erik Doernenburg

Reputation: 3016

In your description above you write "manager category for unit test", but the implementation that follows is not a category, it's an actual implementation of the Manager class, i.e. the code reads

@implementation Manager

and not

@implementation Manager(unitTests)

It seems that the test code uses this second implementation of Manager and that implementation does not have a getOne method. So the mock is right to complain; the implementation of Manager it sees does not have the method and hence it can't stub it.

I believe you can fix your test by making the implementation a category. As far as I know it is possible to override a class method (sharedManager in your case) in a category, but it's a bit dicey to do so. The approach described by Ben Flynn is better.

Upvotes: 1

Ben Flynn
Ben Flynn

Reputation: 18932

Rather than creating a category, you could just stub sharedManager and return a nice mock.

- (void)setUp
{
    [super setUp];

    self.viewController = [ViewController new];

    //for singleton
    id classMockManager = OCClassMock([Manager class]);
    OCMStub([classMockManager sharedManager]).andReturn(classMockManager);
    self.mockManager = classMockManager;
}

I don't have an environment up to test this just this moment, but this strategy should work. Note that this is OCMock3 syntax. See http://ocmock.org/reference/#mocking-class-methods

Upvotes: 3

Related Questions