I\'ve got an app where I push a UIAlertController
with several custom UIAlertAction
s. Each UIAlertAction
performs unique tasks in the
After some playing around I finally figure it out. Turns out that the handler
block can be cast as a function pointer and the function pointer can be executed.
Like so
UIAlertAction *action = myAlertController.actions[0];
void (^someBlock)(id obj) = [action valueForKey:@"handler"];
someBlock(action);
Here's an example of how it would be used.
-(void)test_verifyThatIfUserSelectsTheFirstActionOfMyAlertControllerSomeMethodIsCalled {
//Setup expectations
[[_partialMockViewController expect] someMethod];
//When the UIAlertController is presented automatically simulate a "tap" of the first button
[[_partialMockViewController stub] presentViewController:[OCMArg checkWithBlock:^BOOL(id obj) {
XCTAssert([obj isKindOfClass:[UIAlertController class]]);
UIAlertController *alert = (UIAlertController*)obj;
//Get the first button
UIAlertAction *action = alert.actions[0];
//Cast the pointer of the handle block into a form that we can execute
void (^someBlock)(id obj) = [action valueForKey:@"handler"];
//Execute the code of the join button
someBlock(action);
}]
animated:YES
completion:nil];
//Execute the method that displays the UIAlertController
[_viewControllerUnderTest methodThatDisplaysAlertController];
//Verify that |someMethod| was executed
[_partialMockViewController verify];
}
With a bit of clever casting, I've found a way to do this in Swift (2.2):
extension UIAlertController {
typealias AlertHandler = @convention(block) (UIAlertAction) -> Void
func tapButtonAtIndex(index: Int) {
let block = actions[index].valueForKey("handler")
let handler = unsafeBitCast(block, AlertHandler.self)
handler(actions[index])
}
}
This allows you to call alert.tapButtonAtIndex(1)
in your test and have the correct handler executed.
(I would only use this in my test target, btw)