Search code examples
swiftunit-testinguialertcontrolleruialertaction

Trigger UIAlertAction on UIAlertController programmatically?


There are a couple of existing questions on this topic but they aren't quite what I'm after. I've written a little Swift app rating prompt for my app which presents two UIAlertController instances, one triggered by the other.

I'm now trying to unit test this, and trying to reach that second alert in the tests. I've written a simple spy to check the first controller, but I'd like a way to trigger one of the actions on the first alert, which in turn shows the second.

I've already tried alert.actions.first?.accessibilityActivate(), but it didn't seem to break inside the handler of that action – that's what I'm after.


Solution

  • Here's roughly what I did:

    1. Created a mocked version of my class that would present the alert controller, and in my unit tests, used this mock.

    2. Overrode the following method that I'd created in the non-mocked version:

      func alertActionWithTitle(title: String?, style: UIAlertActionStyle, handler: Handler) -> UIAlertAction
      
    3. In the overridden implementation, stored all the details about the actions in some properties (Handler is just a typealias'd () -> (UIAlertAction))

      var didCreateAlert = false
      var createdTitles: [String?] = []
      var createdStyles: [UIAlertActionStyle?] = []
      var createdHandlers: [Handler?] = []
      var createdActions: [UIAlertAction?] = []
      
    4. Then, when running my tests, to traverse the path through the alerts, I implemented a callHandlerAtIndex method to iterate through my handlers and execute the right one.

    This means that my tests look something like this:

    feedback.start()
    feedback.callHandlerAtIndex(1) // First alert, second action
    feedback.callHandlerAtIndex(2) // Second alert, third action
    XCTAssertTrue(mockMailer.didCallMail)