Unit Testing Your Alerts and Action Sheets

May 18, 2013 — 8 Comments

UIActionSheet example

People assume you can’t write unit tests for user interface code. That just ain’t so. I’ve already shown you how to do UIViewController TDD. Can we do the same for UIAlertView and UIActionSheet? Sure!

This time instead of a screencast, I’ve put my code on GitHub, because you’ll want to incorporate some classes into your tests. Go to my iOSAlertViewActionSheetUnitTesting repository.

The basic trick to writing unit tests of UI code is to recognize that we don’t manage the UI ourselves. Cocoa Touch takes care of that. All we have to do is make sure we’re setting things up correctly for Cocoa Touch, and trust it to do its work. …Do you see the distinction? We don’t need to test Apple’s code, just our code. (This also keeps our tests fast.)

We do need to introduce a seam — that is, a boundary that will allow us to substitute real code with test-specific code. The real code we want to replace is directly invoking UIAlertView or UIActionSheet. Normally, you’d show an alert with code like this:

    UIAlertView *alertView =
        [[UIAlertView alloc] initWithTitle:@"Title"
                                   message:@"Message"
                                  delegate:self
                         cancelButtonTitle:@"Cancel"
                         otherButtonTitles:@"OK", nil];
    [alertView show];

Instead of directly allocating a UIAlertView, what if we allow tests to say, “Don’t make a UIAlertView. Instead, make this mock object. It will record everything I do to it, so I can check all initializer arguments, and make sure show was messaged.”

That’s where JMRMockAlertView and JMRMockActionSheet come in. The repository has an example project showing how to inject them, and how to use the corresponding objects JMRMockAlertViewVerifier and JMRMockActionSheetVerifier to query what happened.

For the sample tests, I put all the assertions together so you can see them all grouped together. But in practice, I break things into separate tests for:

  • Alert should be shown once. Or, the action sheet should be shown once, from a particular view.
  • (Any negative tests, “An alert should not be shown,” or “An action sheet should not be shown.”)
  • Message text
  • Button text
  • What the delegate is

Once you’ve established the delegate, then you can write additional tests that directly invoke the delegate methods you want, simulating different button taps.

Oh, and don’t just copy my end result. Use the “TDD waltz” to build it up gradually, one test at a time. You can see a good order by reading my tests top-down.

Any questions?

Jon Reid

Posts Twitter Facebook Google+

I'm passionate about not just improving our code, but improving the way we code.

8 responses to Unit Testing Your Alerts and Action Sheets

  1. Hi Jon,

    Great post. I don’t have alerts or action sheets to test right now but I really liked your tip on testing via the delegate methods. After reading and watching you other posts and videos I’d realized I could use the delegate and data source method to my table view support code. Nice to know I’m doing it right.

    Thanks!

  2. Sanjeev Dutta July 31, 2013 at 3:35 am

    Hi Jon,

    I really loved all the blogs post you have done on TDD so far and learned a lot from them.
    I have recently been trying to write a unit test for alert view and find this article very helpful.

    Also I have a query related to this. I have a button action which will display an alert if some condition is true, else it will not show the alert. So in scenarios like this when we have conditional display of an alertview or actionsheet, what needs to be done. Can you please guide me on that.

  3. I’m officially a bit stumped.

    I’ve been through the repository a bunch of times and cannot figure out how your mocks are injected into the ViewController. I don’t see an initializer that takes those classes or any method swizzling. How did you do that part?

    Otherwise I really like this.

    • Hi Eric, good to see you here.
      The classes are exposed as properties to support setter injection. The initializer sets them to proper default values — real UIAlertView, real UIActionSheet. The tests set them to JMRMockAlertView and JMRMockActionSheet.
      Maybe you’re not seeing them because you’re looking for dots. I actually had a change of heart recently about dot notation, so I should update the code to reflect this. So look for setAlertViewClass: and setActionSheetClass:.

  4. I think OCMock does a much cleaner job these days, no extra code required. Take a look at http://stackoverflow.com/a/21173592/123094.

Leave a Reply

*

Text formatting is available via select HTML.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> 

Have you Subscribed yet? Don't miss a post!