Unit Testing Your Alerts and Action Sheets

May 18, 2013 — 10 Comments

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!

UIActionSheet example

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.

Do you have any questions? Leave a comment below.

Disclosure of Material Connection: Some of the links in the post above are "affiliate links." This means if you click on the link and purchase the item, I will receive an affiliate commission. Regardless, I only recommend products or services I use personally and believe will add value to my readers. I am disclosing this in accordance with the Federal Trade Commission’s 16 CFR, Part 255: "Guides Concerning the Use of Endorsements and Testimonials in Advertising."

Enjoyed this article? Sign up to get future articles by email.

Email Address:

10 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.

    • OCMock’s partial mocking ability is quite clever.

      • Hi Jon, I’m really curious about how you can achieve this in Swift. Are you still using OCMock?
        Another option that I see is to use DI but it feels awkward to pass an UIAlertView on the initialiser and have to have a UIAlertView property for test purposes while creating it inline is so easier and less complex. And it’s not like I’m going to change the UIAlertView for something else to justify dependency inversion.

        Also I’m really trying to understand how useful is a test that checks if an UIAlertView is shown. Is something easily testable manually and not likely to receive modification from other developers. After watching http://martinfowler.com/articles/is-tdd-dead/ I’m wondering if we are doing a lot of stunts to make something testable. What are your thoughts?

  5. Hi Jon,

    Thank you so much for these awesome screencasts and tips!! I have used your techniques to unit test view controller and XIBs and UIAlertViews successfully :-)
    I have code coverage also integrated in Jenkins and it is great to see the lines
    being covered in the coverage report. I am currently at 40% code coverage on a view controllers that uses UIAlertViews, UIPickerViews and a service and the main challenge is to unit test callbacks from Cocoa Touch. If you have any tips for UITableViews and UIPickerViews please comment. Thanks so much. Keep up the great work. :)

    Amit
    Mint iOS developer.

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!