【问题标题】:When trigged action, mockNavigationController return current viewController, not pushed触发动作时,mockNavigationController 返回当前 viewController,不推送
【发布时间】:2019-09-08 14:53:52
【问题描述】:

我学习 TTD,但在单元测试中遇到导航控制器问题。 当我尝试使用我的模拟控制器通过导航堆栈(pushViewController(ViewController,animated:))推送详细视图控制器时,在测试推送功能中不执行(它仅在第一次执行时,导航控制器初始化)。 在模拟 iPhone 上,应用程序正常工作。 在代码中 mockNavigationController 有值 pushVC,当 pushViewController 执行时会改变。 当用户点击单元格时,dataProvider(tableCell 的委托和数据源)向 ViewController(sut)发布通知,实现 showDetails 方法。

我会尝试从 navigationController 中获取 topViewController: sut.navigationController?.topViewController - 它返回 sut ViewController。 尽量不要在测试中初始化 navigationController。 sut.navigationController?.topViewController - 返回 nil。

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)

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

【问题讨论】:

    标签: ios swift unit-testing uinavigationcontroller mocking


    【解决方案1】:

    @Alexander,欢迎来到 StackOverflow。 ?

    您说dataProvider 是您正在测试的视图控制器中UITableView.dataSource.delegate,它负责启动导航。

    您确定在测试中dataProvider 实际上设置为.dataSource.delegate?如果不是这样,那么启动导航的代码将永远不会被调用。

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

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

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

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

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

    【讨论】:

    • 我对 dataProvider 进行了测试: -testDataProviderIsDataSourceForTableView -testDataProviderIsDelegateForTableView 和 -testWhenViewIsLoadedTableViewDelegateAndDataSourceEquals 它们正确完成了当 mockNC 初始化时,mockPushViewController 用 sut VC 调用。触发showDetails方法,然后调用didSelectRowAt,生成普通VC进行推送。但是在 showDetails 方法中的 navigationController?.pushViewController(detailEaterVC, animated: true) 之后没有再次触发 mockPushViewController。感谢您的回答,我会尝试找出您的建议并申请我的测试。
    • 很高兴能提供帮助:)
    猜你喜欢
    • 2021-10-14
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 2012-04-14
    • 2013-12-18
    • 1970-01-01
    • 2016-01-19
    • 1970-01-01
    相关资源
    最近更新 更多