Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using OCUnit to test if an UIAlertView is presented

I'm working on an app that will display a UIAlertView upon hitting it's exit button, only if progress in the game has been made. I was wondering how you would use OCUnit to intercept the UIAlertView and interact with it, or even detect if it has been presented. The only thing I can think of is to monkeypatch [UIAlertViewDelegate willPresentAlertView], but that makes me want to cry.

Does anyone know of a better method of doing this?

like image 988
wjl Avatar asked May 22 '11 05:05

wjl


2 Answers

Update: See my blog post How to Unit Test Your Alerts and Action Sheets

The problem with my other answer is that the -showAlertWithMessage: method itself is never exercised by unit tests. "Use manual testing to verify it once" isn't too bad for easy scenarios, but error handling often involves unusual situations that are difficult to reproduce. …Besides, I got that nagging feeling that I had stopped short, and that there might be a more thorough way. There is.

In the class under test, don't instantiate UIAlertView directly. Instead, define a method

+ (Class)alertViewClass
{
    return [UIAlertView class];
}

that can be replaced using "subclass and override." (Alternatively, use dependency injection and pass this class in as an initializer argument.)

Invoke this to determine the class to instantiate to show an alert:

Class alertViewClass = [[self class] alertViewClass];
id alert = [[alertViewClass alloc] initWithTitle:...etc...

Now define a mock alert view class. Its job is to remember its initializer arguments, and post a notification, passing itself as the object:

- (void)show
{
    [[NSNotificationCenter defaultCenter] postNotificationName:MockAlertViewShowNotification
                                                        object:self
                                                      userInfo:nil];
}

Your testing subclass (TestingFoo) redefines +alertViewClass to substitute the mock:

+ (Class)alertViewClass
{
    return [MockAlertView class];
}

Make your test class register for the notification. The invoked method can now verify the arguments passed to the alert initializer and the number of times -show was messaged.

Additional tip: In addition to the mock alert, I defined an alert verifier class that:

  • Registers for the notification
  • Lets me set expected values
  • Upon notification, verifies the state against the expected values

So all my alert tests do now is create the verifier, set the expectations, and exercise the call.

like image 102
Jon Reid Avatar answered Sep 22 '22 01:09

Jon Reid


The latest version of OCMock (2.2.1 the at time of this writing) has features that make this easy. Here's some sample test code that stubs UIAlertView's "alloc" class method to return a mock object instead of a real UIAlertView.

id mockAlertView = [OCMockObject mockForClass:[UIAlertView class]];
[[[mockAlertView stub] andReturn:mockAlertView] alloc];
(void)[[[mockAlertView expect] andReturn:mockAlertView]
          initWithTitle:OCMOCK_ANY
                message:OCMOCK_ANY
               delegate:OCMOCK_ANY
      cancelButtonTitle:OCMOCK_ANY
      otherButtonTitles:OCMOCK_ANY, nil];
[[mockAlertView expect] show];

[myViewController doSomething];

[mockAlertView verify];
like image 45
user2816065 Avatar answered Sep 18 '22 01:09

user2816065