2015年8月11日 星期二

Mock completion block with OCMock

This is an example unit test using Specta, Expecta and OCMock.
Another example - WordPress iOS app unit test.

#import "Specta.h"
#define EXP_SHORTHAND
#import "Expecta.h"
#import <OCMock/OCMock.h>
#import "RoomListViewModel.h"
#import <EXTScope.h>

SpecBegin(RoomListViewModel)

describe(@"RoomListViewModel", ^{

    __block RoomListViewModel *viewModel;
    __block id home;
    __block id room;

    beforeAll(^{

    });

    beforeEach(^{
        home = OCMClassMock([HMHome class]);
        room = OCMClassMock([HMRoom class]);
        viewModel = [[RoomListViewModel alloc] initWithHome:home];
    });

    it(@"add room with name", ^{
        OCMStub([home addRoomWithName:OCMOCK_ANY completionHandler:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
            void(^ __unsafe_unretained completionHandler)(HMRoom *room, NSError *error);
            [invocation getArgument:&completionHandler atIndex:3];
            completionHandler(room, nil);
        });    
        waitUntil(^(DoneCallback done) {
            @weakify(viewModel);
            [viewModel addRoomWithName:@"test" completionHandler:^(NSError *error) {
                @strongify(viewModel);
                expect(...);
                done();
            }];
        });

    });  

    afterEach(^{
        viewModel = nil;
    });

    afterAll(^{

    });
});

SpecEnd
void(^ __unsafe_unretained completionHandler)(HMRoom *room, NSError *error);

Why declare our block __unsafe_unretained?
See http://stackoverflow.com/a/13831074/1060971, Carl Lindberg’s comment.

[invocation getArgument:&completionHandler atIndex:3];

In NSInvocation, argument index 0 is self , index 1 is cmd.
First argument of addRoomWithName is at index 2, completion block argument is at index 3.
So, assign our mock block at index 3.

沒有留言:

張貼留言