测试 UIAlertController 是否已经出现
Testing if UIAlertController has been presented
我有一个协议可以让我的 ViewController 显示警报。
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)
}
}
我称之为
@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()
}
)
}
我想编写一个单元测试来断言调用它时,呈现的视图控制器是 UIAlertController
。
我正在尝试类似的方法,但没有通过
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
}
}
您需要等待 UIAlertController
完全可见才能 运行 您的断言。
查看 XCTWaiter。
试试下面的方法:
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")
}
ViewControllerPresentationSpy 通过捕获 将 用于显示警报的信息,而不实际显示任何警报,从而避免缓慢、不稳定的单元测试。您只需要创建一个 AlertVerifier,然后调用显示您的警报的任何内容:
let alertVerifier = AlertVerifier()
sut.show()
alertVerifier.verify(
title: nil,
message: "Are you sure you want to logout?",
animated: true,
presentingViewController: sut,
actions: [
.cancel("No"),
.destructive("Yes"),
]
)
此验证方法检查:
- 显示了一个带有动画的警报。
- 呈现视图控制器是被测系统。
- 警报标题。
- 警报消息。
- UIAlertController.Style的首选样式(默认
.alert
)
- 每个动作的标题和样式。
您可以按名称调用每个操作:
try alertVerifier.executeAction(forButton: "Yes")
(将测试标记为 throws
。如果没有具有给定名称的按钮,测试将失败。)
试试看它与 1.5 秒超时相比有多快。还要比较你能考多少。
我有一个协议可以让我的 ViewController 显示警报。
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)
}
}
我称之为
@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()
}
)
}
我想编写一个单元测试来断言调用它时,呈现的视图控制器是 UIAlertController
。
我正在尝试类似的方法,但没有通过
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
}
}
您需要等待 UIAlertController
完全可见才能 运行 您的断言。
查看 XCTWaiter。
试试下面的方法:
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")
}
ViewControllerPresentationSpy 通过捕获 将 用于显示警报的信息,而不实际显示任何警报,从而避免缓慢、不稳定的单元测试。您只需要创建一个 AlertVerifier,然后调用显示您的警报的任何内容:
let alertVerifier = AlertVerifier()
sut.show()
alertVerifier.verify(
title: nil,
message: "Are you sure you want to logout?",
animated: true,
presentingViewController: sut,
actions: [
.cancel("No"),
.destructive("Yes"),
]
)
此验证方法检查:
- 显示了一个带有动画的警报。
- 呈现视图控制器是被测系统。
- 警报标题。
- 警报消息。
- UIAlertController.Style的首选样式(默认
.alert
) - 每个动作的标题和样式。
您可以按名称调用每个操作:
try alertVerifier.executeAction(forButton: "Yes")
(将测试标记为 throws
。如果没有具有给定名称的按钮,测试将失败。)
试试看它与 1.5 秒超时相比有多快。还要比较你能考多少。