Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test UIAlertController

I'm testing a function that presents a UIAlertController, but the test keeps failing. This is what the function looks like:

func presentBuyingErrorDialogue() {
  let alert = UIAlertController(
    title: "Warning",
    message: "Error purchasing item, please retry this action. If that doesn't help, try restarting or reinstalling the app.",
    preferredStyle: .alert
  )
  let okButton = UIAlertAction(title: "OK", style: .default)
  alert.addAction(okButton)
  self.present(alert, animated: true, completion: nil)
}

Since this function is in a class called ShopViewController, I'm assuming that the correct way to test this would be to call the function shopViewController.presentBuyingErrorDiaologue() then use XCTAssertTrue(shopViewController.presentedViewController is UIAlertController). However, the assert statement fails when I run the test. What would be the correct way to test that the UIAlertController is the presented view?

like image 211
Erika Avatar asked Jan 14 '18 05:01

Erika


2 Answers

You should wait that UIAlertController is fully presented before testing its visibility, so you might try to change your test following this way:

import XCTest

class UIAlertControllerTests: XCTestCase {

    func presentBuyingErrorDialogue() {
      let alert = UIAlertController(title: "Warning", message: "Error purchasing item, please retry this action. If that doesn't help, try restarting or reinstalling the app.", preferredStyle: .alert)
      let okButton = UIAlertAction(title: "OK", style: .default)
      alert.addAction(okButton)
      UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
    }

    func testPresentBuyingErrorDialogue() {
      self.presentBuyingErrorDialogue()
      let expectation = XCTestExpectation(description: "testExample")
      DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
        XCTAssertTrue(UIApplication.shared.keyWindow?.rootViewController?.presentedViewController is UIAlertController)
        expectation.fulfill()
      })
      wait(for: [expectation], timeout: 1.5)
   }
}

You may change UIApplication.shared.keyWindow?.rootViewController with your ShopViewController.

like image 100
Andrea Mugnaini Avatar answered Oct 06 '22 01:10

Andrea Mugnaini


  1. Add ViewControllerPresentationSpy to your test target
  2. import ViewControllerPresentationSpy
  3. Instantiate an AlertVerifier before your test calls shopViewController.presentBuyingErrorDialogue()

Then call its verify method:

alertVerifier.verify(
    title: "Warning",
    message: "Error purchasing item, please retry this action. If that doesn't help, try restarting or reinstalling the app.",
    animated: true,
    presentingViewController: shopViewController,
    actions: [
        .default("OK"),
    ]
)

This checks:

  • That one alert was presented, with animation.
  • That the presenting view controller was the shopViewController.
  • The alert title.
  • The alert message.
  • That the preferred style was .alert (by default)
  • The titles and styles of each action.

In addition to capturing values, AlertVerifier lets you easily execute the action for an alert button:

func test_alertOKButton() throws {
    shopViewController.presentBuyingErrorDiaologue()

    try alertVerifier.executeAction(forButton: "OK")

    // Test the results here
}

The tests don't require any waiting for expectations, so they are super-fast.

like image 44
Jon Reid Avatar answered Oct 06 '22 01:10

Jon Reid