Here's the method under test:
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {
NSDictionary *userPassD = @{@"user":userName,
@"pass":pass};
[_loginCntrl loginWithUserPass:userPassD withSuccess:^(NSString *authToken){
// save authToken to credential store
} failure:^(NSString *errorMessage) {
// alert user pass was wrong
}];
}
what I want to test is that in that success block the other dependency/OCMockObject _credStore is called with the appropriate methods. So currently the loginCtrl and credStore dependencies are OCMockObjects and I can stub/expect on those.
Would I stub loginController to somehow execute that block when called? I've looked at some of the questions on stubbing blocks with OCMock and I can't wrap my head around what they're doing and if it would be suitable for this situation.
In reality all I want to do is OCMock to fire the block ([success invoke]??) so that the code _credStore saveUserPass is done and can be verified on _credStore.
where I stopped:
- (void)test_loginWithuserPass_succeeds_should_call_credStore_setAuthToken {
NSDictionary *userPassD = @{@"user":@"mark",
@"pass":@"test"};
id successBlock = ^ {
// ??? isn't this done in the SUT?
};
[[[_loginController stub] andDo:successBlock] loginWithUserPass:userPassD withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
[[_credentialStore expect] setAuthToken:@"passed back value from block"];
[_docServiceSUT loginWithUser:@"mark" andPass:@"test"];
[_credentialStore verify];
}
ETA: here's what I have based on Ben's example below, but not working, getting a EXC_BAD_ACCESS exception:
// OCUnit test method
- (void)test_loginWithUserPass_success_block_should_call_credentials_setAuthToken {
void (^proxyBlock)(NSInvocation*) = ^(NSInvocation *invocation) {
void(^successBlock)(NSString *authToken);
[invocation getArgument:&successBlock atIndex:3]; // should be 3 because my block is the second param
successBlock(@"myAuthToken");
};
[[[_loginController expect] andDo:proxyBlock] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
[[_credentialStore expect] setAuthToken:@"myAuthToken"];
[_docServiceSUT loginWithUser:@"mark" andPass:@"myPass"];
[_loginController verify];
[_credentialStore verify];
}
//method under test
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {
NSDictionary *userPassD = @{@"user":userName,
@"pass":pass};
void(^onSuccess)(NSString *) = ^(NSString *authToken){
[SVProgressHUD dismiss];
[_credentials setAuthToken:authToken];
// Ask user to enter the 6 digit authenticator key
[self askUserForAuthenticatorKey];
};
void(^onFailure)(NSString *) = ^(NSString *errorMessage) {
[SVProgressHUD dismiss];
[_alertSender sendAlertWithMessage:errorMessage andTitle:@"Login failed"];
};
[SVProgressHUD show];
[_loginCntrl loginWithUserPass:userPassD withSuccess:onSuccess
failure:onFailure];
}
I think you can do this with a spy. Reading this wiki page on spies it looks like you can capture the block passed in and invoke it yourself in the test.
If I follow you right, this may do what you want:
This code allows forces the real success block to be invoked with the argument you specify, which you can then verify.
Code I'm working with is heavily based on blocks, so I'm super familiar with your question.
Just to rephrase problem a bit:
To solve exactly the same problem you're bringing up here, we've created unit test helper class that has methods with identical signature to methods that call blocks that we need to test.
For you code sample, it is a mock method that returns nothing, takes one id argument and two blocks.
Below is example of the mock method you'd need and sample of the OCMock unit test that utilizes
Hope it helps! Let me know if you have any questions, we've got it working.