Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stub method block in Kiwi?

I want to stub a method which takes a block as a parameter using Kiwi. Here is the full explanation with code:

I have a class named TestedClass which has a method testedMethod which dependent on class NetworkClass which calls via AFNetworking to a server, and return its response via block. Translating to code:

@interface TestedClass : NSObject
    -(void)testMethod;
@end

-(void)testMethod
{
    NetworkClass *networkClass = [[NetworkClass alloc] init];

    [networkClass networkMethod:^(id result)
    {
        // code that I want to test according to the block given which I want to stub
        ...
    }];
}



typedef void (^NetworkClassCallback)(id result);

 @interface NetworkClass : NSObject
 -(void)networkMethod:(NetworkClassCallback)handler;
 @end

-(void) networkMethod:(NetworkClassCallback)handler
{
    NSDictionary *params = @{@"param":@", @"value"};
    NSString *requestURL = [NSString stringWithFormat:@"www.someserver.com"];
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURLURLWithString:requestURL]];
    NSURLRequest *request = [httpClient requestWithMethod:@"GET" path:requestURL parameters:params];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
         handler(responseObject);
    }

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    handler(nil);
}];

[operation start];
}

How can I use Kiwi to stub networkMethod with block in order to unit test testMethod?

UPDATE: Found how to do this in Kiwi, see my answer below.

like image 745
kernix Avatar asked Oct 16 '13 08:10

kernix


1 Answers

Here is how you do this in Kiwi:

First, you must dependency inject NetworkClass to TestedClass (if it's not clear how, please add a comment and I'll explain; this can be done as a property for simplicity. This is so that you can operate on a mock object for the NetworkClass)

Then your spec, create the mock for the network class and create your class that you want to unit test:

SPEC_BEGIN(TestSpec)

describe(@"describe goes here", ^{
    it(@"should test block", ^{
        NetworkClass *mockNetworkClass = [NetworkClass mock];
        KWCaptureSpy *spy = [mockNetworkClass captureArgument:@selector(networkMethod:) atIndex:0];
        TestedClass testClass = [TestedClass alloc] init];
        testClass.networkClass = mockNetworkClass;
        [testClass testMethod];

        NetworkClassCallback blockToRun = spy.argument;
        blockToRun(nil);

        // add expectations here

    });
});

SPEC_END

To explain what's going on here:

You are creating TestedClass and calling testMethod. However, before that, we are creating something called Spy - its job is to capture the block in the first parameter when networkMethod: is called. Now, it's time to actually execute the block itself.

It's easy to be confused here so I'll emphasize this: the order of calls is important; you first declare the spy, then call the tested method, and only then you're actually calling and executing the block!

This will give you the ability to check what you want as you're the one executing the block.

Hope it helps for other, as it took me quite sometime to understand this flow.

like image 142
kernix Avatar answered Nov 10 '22 14:11

kernix