ViewControllerPresentationSpy
ViewControllerPresentationSpy 有三个验证器
AlertVerifier
用来捕获弹窗和操作表单PresentationVerifier
用来捕获已展示的视图控制器DismissalVerifier
用来捕获已消失的视图控制器
可以捕获 Segues。实际上并不会展示或消失任何东西。这意味着
- 工作流程不会暂停等待弹窗动作的选择。
- 测试速度非常快。
- 您可以使用单元测试而不是 UI 测试来测试东西。
关于具体示例,请参阅 iOS 单元测试实例: 第 9 章 "测试弹窗" 和第 10 章 "测试屏幕间的导航"。
目录
编写测试
生产代码中我需要修改什么?
什么都不需要。
我如何测试 alert 控制器?
- 在测试的 Act 阶段之前,实例化一个
AlertVerifier
。 - 调用代码创建并展示你的 alert 或操作表。
有关 alert 或操作表的信息可通过 AlertVerifier 获取。
例如,这里有验证以下内容的测试:
- 展示了一个带有动画的 alert。
- 展示的视图控制器是待测试的系统。
- alert 标题。
- alert 消息。
- UIAlertController.Style 的偏好样式。
- 每个动作的标题和样式。
sut
是测试 fixtures 中的待测试的系统。Swift 版本使用了一个方便的 verify
方法。
func test_showAlert_alertShouldHaveTitle() {
let alertVerifier = AlertVerifier()
sut.showAlert() // Whatever triggers the alert
alertVerifier.verify(
title: "Hello!",
message: "How are you?",
animated: true,
presentingViewController: sut,
actions: [
.default("OK"),
.cancel("Cancel"),
]
)
}
- (void)test_showAlert_alertShouldHaveTitle
{
QCOAlertVerifier *alertVerifier = [[QCOAlertVerifier alloc] init];
[sut showAlert]; // Whatever triggers the alert
XCTAssertEqual(alertVerifier.presentedCount, 1, @"presented count");
XCTAssertEqualObjects(alertVerifier.title, @"Hello!", @"title");
XCTAssertEqualObjects(alertVerifier.message, @"How are you?", @"message");
XCTAssertEqual(alertVerifier.animated, YES, @"animated");
XCTAssertEqual(alertVerifier.preferredStyle, UIAlertController.Style.alert, @"preferred style");
XCTAssertEqual(alertVerifier.presentingViewController, sut, @"presenting view controller");
XCTAssertEqual(alertVerifier.actions.count, 2, @"actions count);
XCTAssertEqualObjects(alertVerifier.actions[0].title, @"OK", @"first action");
XCTAssertEqual(alertVerifier.actions[0].style, UIAlertActionStyleDefault, @"first action");
XCTAssertEqualObjects(alertVerifier.actions[1].title, @"Cancel", @"second action");
XCTAssertEqual(alertVerifier.actions[1].style, UIAlertActionStyleCancel, @"second action");
}
我如何调用与 UIAlertAction 相关的闭包?
按照上述步骤展示你的 alert 或操作表。然后在你的 AlertVerifier
上调用 executeAction (forButton:)
并传入按钮标题。例如:
func test_executingActionForOKButton_shouldDoSomething() throws {
let alertVerifier = AlertVerifier()
sut.showAlert()
try alertVerifier.executeAction(forButton: "OK")
// Now assert what you want
}
- (void)test_executingActionForOKButton_shouldDoSomething
{
QCOAlertVerifier *alertVerifier = [[QCOAlertVerifier alloc] init];
[sut showAlert];
NSError *error = nil;
[alertVerifier executeActionForButton:@"OK" andReturnError:&error];
XCTAssertNil(error);
// Now add your own assertions
}
因为这个方法可能会抛出异常,所以声明 Swift 测试方法为 throws
并使用 try
调用该方法。对于 Objective-C,传入一个 NSError 并检查它是否不为 nil。
我如何测试展示的视图控制器?
- 在测试的 Act 阶段之前,实例化一个
PresentationVerifier
。 - 调用代码创建并展示你的视图控制器。
然后通过 PresentationVerifier 获取展示信息。
例如,这里有验证以下内容的测试:
- 展示了一个带有动画的视图控制器。
- 展示的视图控制器是待测试的系统。
- 展示的视图控制器类型正确。
- 展示的视图控制器具有特定属性。
sut
是测试 fixtures 中的待测试的系统。Swift 版本使用了一个方便的 verify
方法。
func test_presentedVC_shouldHaveSpecialSettingHello() {
let presentationVerifier = PresentationVerifier()
sut.showVC() // Whatever presents the view controller
let nextVC: MyViewController? = presentationVerifier.verify(animated: true,
presentingViewController: sut)
XCTAssertEqual(nextVC?.specialSetting, "Hello!")
}
- (void) test_presentedVC_shouldHaveSpecialSettingHello
{
QCOPresentationVerifier *presentationVerifier = [[QCOPresentationVerifier alloc] init];
[sut showVC]; // Whatever presents the view controller
XCTAssertEqual(presentationVerifier.presentedCount, 1, @"presented count");
XCTAssertTrue(presentationVerifier.animated, @"animated");
XCTAssertEqual(presentationVerifier.presentingViewController, sut, @"presenting view controller");
if (![presentationVerifier.presentedViewController isKindOfClass:[MyViewController class]])
{
XCTFail(@"Expected MyViewController, but was %@", presentationVerifier.presentedViewController);
return;
}
MyViewController *nextVC = presentationVerifier.presentedViewController;
XCTAssertEqualObjects(nextVC.specialSetting, @"Hello!");
}
如何测试segue?
这取决于具体情况。首先,按照上述步骤测试显示的视图控制器。在测试代码中触发segue。例如,我们可以通过在按钮上调用sendActions(for: .touchUpInside)
来触发与按钮关联的segue。
Segue类型:模态呈现
这就做完了。但你需要注意一个内存问题:
在测试执行过程中, neither the presenting view controller nor the presented view controller will be deallocated. 这可能会在测试过程中出现问题的原因,如果其中任何一个影响全局状态,例如监听NotificationCenter。你可能需要在deinit
之外添加特殊的方法,使测试能够清理它们。
Segue类型:显示
“显示”segue(它执行推送导航)需要做更多工作。
首先,将呈现视图控制器安装为UIWindow的根视图控制器。使此窗口可见。
let window = UIWindow()
window.rootViewController = sut
window.isHidden = false
要清理内存,请将以下内容添加到测试套件的tearDown()
方法的开头以泵送运行循环
RunLoop.current.run(until: Date())
这确保了视图窗口在测试用例结束时被释放。这样,视图控制器也将不再存在。
如何测试 dismiss 模态视图?
- 在测试的Act阶段之前实例化一个
DismissalVerifier
。 - 调用 dismiss 模态的代码。
有关 dismiss 的信息然后可以通过DismissalVerifier获取。
例如,这是一个测试,用于验证特定的视图控制器是否已 dismiss,带有动画。
sut
是测试 fixtures 中的待测试的系统。Swift 版本使用了一个方便的 verify
方法。
func test_dismissingVC() {
let dismissalVerifier = DismissalVerifier()
sut.dismissVC() // Whatever dismisses the view controller
dismissalVerifier.verify(animated: true, dismissedViewController: sut)
}
- (void) test_dismissingVC
{
QCODismissalVerifier *dismissalVerifier = [[QCODismissalVerifier alloc] init];
[sut dismissVC]; // Whatever dismisses the view controller
XCTAssertEqual(dismissalVerifier.dismissedCount, 1, @"dismissed count");
XCTAssertTrue(dismissalVerifier.animated, @"animated");
XCTAssertEqual(dismissalVerifier.presentingViewController, sut, @"dismissed view controller");
}
如何调用传递给 present 或 dismiss 的闭包?
生产代码的完成处理器被捕捉在验证器的capturedCompletion
属性中。
如何测试使用 DispatchQueue.main 展示或取消的视图内容?
在您的测试用例中创建一个期待。在验证器的 testCompletion
闭包中实现它。在断言阶段开始时添加一个短暂的等待。
func test_showAlertOnMainDispatchQueue_shouldDoSomething() {
let alertVerifier = AlertVerifier()
let expectation = self.expectation(description: "alert presented")
alertVerifier.testCompletion = { expectation.fulfill() }
sut.showAlert()
waitForExpectations(timeout: 0.001)
// Now assert what you want
}
func test_presentViewControllerOnMainDispatchQueue_shouldDoSomething() {
let presentationVerifier = PresentationVerifier()
let expectation = self.expectation(description: "view controller presented")
presentationVerifier.testCompletion = { expectation.fulfill() }
sut.showVC()
waitForExpectations(timeout: 0.001)
// Now assert what you want
}
能给我看看一些例子吗?
Swift 和 Objective-C 中都有示例应用程序。在手机和平板上运行它们,看看它们做了什么,然后阅读 ViewControllerAlertTests 和 ViewControllerPresentationTests。
如何将 ViewControllerPresentationSpy 添加到我的项目中?
Swift Package Manager
在您的 Package.swift 清单依赖项数组的包中包含 ViewControllerPresentationSpy 包
dependencies: [
.package(
url: "https://github.com/jonreid/ViewControllerPresentationSpy",
.upToNextMajor(from: "7.0.0")
),
],
CocoaPods
将以下内容添加到您的 Podfile 中,将 "MyTests" 改为您的测试目标名称
target 'MyTests' do
inherit! :search_paths
pod 'ViewControllerPresentationSpy', '~> 7.0'
end
Carthage
将以下内容添加到您的 Cartfile 中
github "jonreid/ViewControllerPresentationSpy" ~> 7.0
预构建框架
在 GitHub 上可提供预构建的二进制文件。该二进制文件被打包为 ViewControllerPresentationSpy.xcframework,包含以下架构:
- Mac Catalyst
- iOS 设备
- iOS 模拟器
- tvOS 设备
- tvOS 模拟器
将 XCFramework 拖到您的项目中。
自行构建
如果您想自己构建 ViewControllerPresentationSpy,请克隆仓库,然后
$ cd Source
$ ./MakeDistribution.sh