当触发动作时,moreNavigationController return current viewController, not pushed

When trigged action, mockNavigationController return current viewController, not pushed

我正在学习 TTD,但单元测试中的导航控制器有问题。 当我尝试使用我的模拟控制器通过导航堆栈 (pushViewController(ViewController, animated:) 推送 Detail View Controller 时,测试推送功能不执行(它只执行第一次,当导航控制器初始化)。 在模拟 iPhone 中,应用程序正常运行。 在代码中,mockNavigationController 具有值 pushedVC,当 pushViewController 执行时会发生变化。 当用户点击单元格时,dataProvider(tableCell 的委托和数据源)post 通知 ViewController (sut),实现 showDetails 方法。

我会尝试从 navigationController 获取 topViewController: sut.navigationController?.topViewController - 它是 return 和 ViewController。 尽量不要在测试中初始化 navigationController。 sut.navigationController?.topViewController - return 无。

XCTestCase的开头

var sut: EatersListViewController!

    override func setUp() {
        super.setUp()

        let storyBoard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyBoard.instantiateViewController(withIdentifier: String(describing: EatersListViewController.self))
        sut = vc as? EatersListViewController

        sut.loadViewIfNeeded()
    }

这个测试函数

func testSelectedRowPushedDetailVC() {
        let mockNavigationController = MockNavigationController(rootViewController: sut)
        UIApplication.shared.keyWindow?.rootViewController = mockNavigationController

        let eater1 = Eater(name: "Foo")
        sut.dataProvider.manager!.addEater(eater: eater1)

        sut.loadViewIfNeeded()

        sut.tableView.delegate?.tableView?(sut.tableView, didSelectRowAt: IndexPath(row: 0, section: 0))

        guard let detailEaterVC = mockNavigationController.pushedVC as? DetailEaterViewController else {
            XCTFail()
            return
        }
        detailEaterVC.loadViewIfNeeded()

        XCTAssertNotNil(detailEaterVC.eaterNameLabel)
        XCTAssertEqual(detailEaterVC.eaterData, eater1)
}

这个函数来自 ViewController

@objc func showDetails(withNotification notification: Notification) {
        guard
            let userInfo = notification.userInfo,
            let eater = userInfo["eater"] as? Eater,
            let detailEaterVC = storyboard?.instantiateViewController(withIdentifier: String(describing: DetailEaterViewController.self)) as? DetailEaterViewController else { return }
        detailEaterVC.eaterData = eater
        navigationController?.pushViewController(detailEaterVC, animated: true)
}

和 MockNavigationController

extension EatersListViewControllerTests {
    class MockNavigationController: UINavigationController {

        var pushedVC: UIViewController?

        override func pushViewController(_ viewController: UIViewController, animated: Bool) {
            pushedVC = viewController
            super.pushViewController(viewController, animated: animated)
        }
    }
}

我原以为 XCTAssert 可以正常工作,但每次在 XCTFail() 行上的测试都失败了。我觉得哪里出错了,不知道这里。

XCTAssertNotNil(detailEaterVC.eaterNameLabel)
XCTAssertEqual(detailEaterVC.eaterData, eater1)

需要代码方面的帮助,但我错了。感谢阅读。

您好 @Alexander,欢迎来到 Whosebug。

你说 dataProvider.dataSource.delegateUITableView 在你的测试视图控制器中,它负责启动导航.

您确定在测试中 dataProvider 实际上设置为 .dataSource.delegate 吗?如果不是这种情况,则永远不会调用启动导航的代码。

您可以使用断点来验证两件事:

  1. 如果你的showDetails方法被调用
  2. 如果调用 MockNavigationController 中的 pushViewController(_:, animated:) 方法

我猜其中一个未被调用,这可能会指出问题的原因。

如果允许的话,多说几句:

  • 我建议使用 NavigationDelegate pattern 来测试此行为。通过删除 fiddle 和 UIApplication.
  • 的需要,它会让你更简单
  • 在测试中最好使用_ = sut.viewsut.beginAppearanceTransition(true, animated: false)来触发视图控制器视图的设置