You can get unit tests for alert views fairly seamlessly by exchanging the 'show' implementation of UIAlertView. For example, this interface gives you some amount of testing abilities:
@interface UIAlertView (Testing)
+ (void)skipNext;
+ (BOOL)didSkip;
@end
with this implementation
#import <objc/runtime.h>
@implementation UIAlertView (Testing)
static BOOL skip = NO;
+ (id)alloc
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method showMethod = class_getInstanceMethod(self, @selector(show));
Method show_Method = class_getInstanceMethod(self, @selector(show_));
method_exchangeImplementations(showMethod, show_Method);
});
return [super alloc];
}
+ (void)skipNext
{
skip = YES;
}
+ (BOOL)didSkip
{
return !skip;
}
- (void)show_
{
NSLog(@"UIAlertView :: would appear here (%@) [ title = %@; message = %@ ]", skip ? @"predicted" : @"unexpected", [self title], [self message]);
if (skip) {
skip = NO;
return;
}
}
@end
You can write unit tests e.g. like this:
[UIAlertView skipNext];
// do something that you expect will give an alert
STAssertTrue([UIAlertView didSkip], @"Alert view did not appear as expected");
If you want to automate tapping a specific button in the alert view, you will need some more magic. The interface gets two new class methods:
@interface UIAlertView (Testing)
+ (void)skipNext;
+ (BOOL)didSkip;
+ (void)tapNext:(NSString *)buttonTitle;
+ (BOOL)didTap;
@end
which go like this
static NSString *next = nil;
+ (void)tapNext:(NSString *)buttonTitle
{
[next release];
next = [buttonTitle retain];
}
+ (BOOL)didTap
{
BOOL result = !next;
[next release];
next = nil;
return result;
}
and the show method becomes
- (void)show_
{
if (next) {
NSLog(@"UIAlertView :: simulating alert for tapping %@", next);
for (NSInteger i = 0; i < [self numberOfButtons]; i++)
if ([next isEqualToString:[self buttonTitleAtIndex:i]]) {
[next release];
next = nil;
[self alertView:self clickedButtonAtIndex:i];
return;
}
return;
}
NSLog(@"UIAlertView :: would appear here (%@) [ title = %@; message = %@ ]", skip ? @"predicted" : @"unexpected", [self title], [self message]);
if (skip) {
skip = NO;
return;
}
}
This can be tested similarly, but instead of skipNext you'd say which button to tap. E.g.
[UIAlertView tapNext:@"Download"];
// do stuff that triggers an alert view with a "Download" button among others
STAssertTrue([UIAlertView didTap], @"Download was never tappable or never tapped");