ViewControllerPresentationSpy

Build Status Carthage compatible Mastodon Follow

ViewControllerPresentationSpy 有三个验证器

可以捕获 Segue。实际上没有任何东西被呈现或解除。这意味着

有关具体示例,请参阅iOS 单元测试示例: 第 9 章“测试警报”和第 10 章“屏幕间导航测试”。

目录

编写测试

我需要在生产代码中更改什么?

无。

如何测试警报控制器?

  1. 在测试的 Act 阶段之前实例化一个 AlertVerifier
  2. 调用代码以创建和呈现你的警报或操作表。

有关警报或操作表的信息随后可通过 AlertVerifier 获得。

例如,这是一个验证的测试

sut 是测试装置中的 System Under Test。 Swift 版本使用方便的 verify 方法。

@MainActor
func test_showAlert_alertShouldHaveTitle() {
    let alertVerifier = AlertVerifier()

    sut.showAlert() // Whatever triggers the alert

    alertVerifier.verify(
        title: "Hello!",
        message: "How are you?",
        animated: true,
        actions: [
            .default("OK"),
            .cancel("Cancel"),
        ],
        presentingViewController: sut
    )
}
- (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");
    XCTAssertIdentical(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 关联的闭包?

按照上述步骤呈现你的警报或操作表。 然后使用按钮标题在你的 AlertVerifier 上调用 executeAction (forButton:)。 例如

@MainActor
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。

如何测试呈现的视图控制器?

  1. 在测试的 Act 阶段之前实例化一个 PresentationVerifier
  2. 调用代码以创建和呈现你的视图控制器。

有关演示的信息随后可通过 PresentationVerifier 获得。

例如,这是一个验证的测试

sut 是测试装置中的 System Under Test。 Swift 版本使用方便的 verify 方法。

@MainActor
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");
    XCTAssertIdentical(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 类型:模态呈现

这就是你需要做的。但是你需要注意一个内存问题:

在测试执行期间,呈现视图控制器和被呈现视图控制器都不会被释放。 如果两者都影响全局状态(例如侦听 NotificationCenter),则这可能会在测试运行期间导致问题。 你可能需要在 deinit 之外添加特殊方法,以允许测试清理它们。

Segue 类型:显示

“显示”Segue(进行推送导航)需要更多的工作。

首先,将呈现视图控制器作为 UIWindow 的根视图控制器安装。 使此窗口可见。

let window = UIWindow()
window.rootViewController = sut
window.isHidden = false

为了在最后清理内存,请将此添加到测试套件的 tearDown() 方法的开头,以泵送运行循环

RunLoop.current.run(until: Date())

这确保了窗口在测试用例结束时被释放。 这样,两个视图控制器也将不复存在。

如何测试解除模态窗口?

  1. 在测试的 Act 阶段之前实例化一个 DismissalVerifier
  2. 调用代码以解除你的模态窗口。

有关解除的信息随后可通过 DismissalVerifier 获得。

例如,这是一个验证特定视图控制器是否被解除(带有动画)的测试。

sut 是测试装置中的 System Under Test。 Swift 版本使用方便的 verify 方法。

@MainActor
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");
    XCTAssertIdentical(dismissalVerifier.dismissedViewController, sut, @"dismissed view controller");
}

如何调用传递给 present 或 dismiss 的闭包?

生产代码完成处理程序在验证器的 capturedCompletion 属性中捕获。

如何测试在 Task 中或使用 DispatchQueue.main 呈现或解除的内容?

在你的测试用例中创建一个 expectation。 在验证器的 testCompletion 闭包中实现它。 在 Assert 阶段的开始处添加一个简短的 waitForExpectations

@MainActor
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
}
@MainActor
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,其中包含以下架构

将 XCFramework 拖到你的项目中。

自己构建

如果你想自己构建 ViewControllerPresentationSpy,请克隆 repo,然后

$ cd Source
$ ./MakeDistribution.sh

作者

Jon Reid 是iOS 单元测试示例的作者。 他的网站是 Quality Coding