How to Unit Test Your Alerts and Action Sheets 

 May 18, 2013

by Jon Reid


Update: UIAlertView is deprecated. Check out how to test UIAlertControllers.

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

Jon Reid

About the author

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code lead to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 20 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy. Now a coach with Industrial Logic!

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


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

  • 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:.

      • 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?

        • Raphael, my code should work with Swift because it doesn’t use introspection. But it still requires you to inject the JMRMockAlertView.

          Note that DI doesn’t mean you have to pass UIAlertView into the initializer. Use property injection instead, with a lazy getter that establishes UIAlertView as the default.

          A big benefit of jumping through these hoops is development speed. In order to pop up an alert manually, you have to navigate to the portion in your app that does so. How many steps does that take? I can do it simply by running tests. Then once it’s under test, I have the ability to refactor the code whenever I please — again, without manual testing.

  • 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. :)

    Mint iOS developer.

    • Amit, I’m glad to hear you’re successfully testing UI code — that’s great!

      The main thing with Cocoa Touch callbacks is you have to have your test directly invoke the callback, instead of having Cocoa Touch do so. For example, call -tableView:didSelectRowAtIndexPath: to test what happens upon selection.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}