I have a protocol I use to allow my ViewControllers to present an alert.
import UIKit
struct AlertableAction {
var title: String
var style: UIAlertAction.Style
var result: Bool
}
protocol Alertable {
func presentAlert(title: String?, message: String?, actions: [AlertableAction], completion: ((Bool) -> Void)?)
}
extension Alertable where Self: UIViewController {
func presentAlert(title: String?, message: String?, actions: [AlertableAction], completion: ((Bool) -> Void)?) {
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccurred()
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
actions.forEach { action in
alertController.addAction(UIAlertAction(title: action.title, style: action.style, handler: { _ in completion?(action.result) }))
}
present(alertController, animated: true, completion: nil)
}
}
I call this something like
@objc private func didTapLogout() {
presentAlert(
title: nil, message: "Are you sure you want to logout?",
actions: [
AlertableAction(title: "No", style: .cancel, result: false),
AlertableAction(title: "Yes", style: .destructive, result: true),
],
completion: { [weak self] result in
guard result else { return }
self?.presenter.logout()
}
)
}
I'd like to write a unit test to assert when this is called, the presented view controller is UIAlertController
.
I was trying something like, but it does not pass
func test_renders_alert_controller() {
sut.show()
XCTAssertNotNil(sut.presentedViewController)
}
class MockViewController: UIViewController, Alertable {
var presentViewControllerTarget: UIViewController?
func show() {
presentAlert(title: nil, message: "Are you sure you want to logout?", actions:
[AlertableAction(title: "No", style: .cancel, result: false)],
completion: nil
)
self.presentViewControllerTarget = self.presentedViewController
}
}
You need to wait for the UIAlertController
to be fully visible before running your assertion.
Check out XCTWaiter.
Try something like the below:
let nav = UINavigationController.init(rootViewController: sut)
sut.show()
let exp = expectation(description: "Test after 1.5 second wait")
let result = XCTWaiter.wait(for: [exp], timeout: 1.5)
if result == XCTWaiter.Result.timedOut {
XCTAssertNotNil(nav.visibleViewController is UIAlertController)
} else {
XCTFail("Delay interrupted")
}