ViewControllerPresentationSpy 7.0.0

ViewControllerPresentationSpy 7.0.0

Jon Reid 维护。



ViewControllerPresentationSpy

Build Status Carthage compatible CocoaPods Version Twitter Follow

ViewControllerPresentationSpy 有三个验证器

  • AlertVerifier 用来捕获弹窗和操作表单
  • PresentationVerifier 用来捕获已展示的视图控制器
  • DismissalVerifier 用来捕获已消失的视图控制器

可以捕获 Segues。实际上并不会展示或消失任何东西。这意味着

  • 工作流程不会暂停等待弹窗动作的选择。
  • 测试速度非常快。
  • 您可以使用单元测试而不是 UI 测试来测试东西。

关于具体示例,请参阅 iOS 单元测试实例 第 9 章 "测试弹窗" 和第 10 章 "测试屏幕间的导航"。

目录

编写测试

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

什么都不需要。

我如何测试 alert 控制器?

  1. 在测试的 Act 阶段之前,实例化一个 AlertVerifier
  2. 调用代码创建并展示你的 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。

我如何测试展示的视图控制器?

  1. 在测试的 Act 阶段之前,实例化一个 PresentationVerifier
  2. 调用代码创建并展示你的视图控制器。

然后通过 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 模态视图?

  1. 在测试的Act阶段之前实例化一个DismissalVerifier
  2. 调用 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