How to simulate the result of a block as a method parameter?

advertisements

I have a method that triggers an asynchronous request in the model and passes a block that handles the response:

[user loginWithEmail:self.eMailTextField.text
         andPassword:self.passwordTextField.text
               block:^(UserLoginResponse response) {
                   switch (response) {
                       case UserLoginResponseSuccess:
                       {
                           // hooray
                           break;
                       }
                       case UserLoginResponseFailureAuthentication:
                           // bad credentials
                           break;
                       case UserLoginResponseFailureB:
                           // failure reason b
                           break;
                       default:
                           // unknown error
                           break;
                   }
               }];

The called method sets some parameters for the request and uses AFNetworking to start it.

Now I want to write a unit test to make sure the calling class reacts properly for every possible UserLoginResponse. I'm using Kiwi for testing but I think this is more of a general question...

How would I mock the argument that gets passed to the block from the user object? The only way I can think of is to mock the underlying request and return the status code I expect for the test. Is there a better way?

It would also be possible to replace the block by using a delegate but I would definitely prefer using blocks here.


It seems like there are 2 distinct things you want to verify here: 1) the user object passes the actual response to the block, and 2) the block handles various response codes appropriately.

For #1, it seems like the right approach is to mock the request (the example uses OCMock and Expecta syntax):

[[[request expect] andReturn:UserLoginResponseSuccess] authenticate];

__block UserLoginResponse actual;

[user loginWithEmail:nil
         andPassword:nil
               block:^(UserLoginResponse expected) {
                   actual = expected;
               }];

expect(actual).toEqual(UserLoginResponseSuccess);

For #2, I'd create a method that returns the block you want to validate. Then you can test it directly without all the other dependencies:

In your header:

typedef void(^AuthenticationHandlerBlock)(UserLoginResponse);
-(AuthenticationHandlerBlock)authenticationHandler;

In your implementation:

-(AuthenticationHandlerBlock)authenticationHandler {
    return ^(UserLoginResponse response) {
       switch (response) {
           case UserLoginResponseSuccess:
           {
               // hooray
               break;
           }
           case UserLoginResponseFailureAuthentication:
               // bad credentials
               break;
           case UserLoginResponseFailureB:
               // failure reason b
               break;
           default:
               // unknown error
               break;
       }
    }
}

In your test:

AuthenticationHandlerBlock block = [user authenticationHandler];
block(UserLoginResponseSuccess);
// verify success outcome
block(UserLoginResponseFailureAuthentication);
// verify failure outcome
block(UserLoginResponseFailureB);
// verify failure B outcome