当触发动作时,mockNavigationController返回当前的viewController,而不是推送

时间:2019-04-18 09:33:06

标签: ios swift unit-testing uinavigationcontroller mocking

我正在学习TTD,并且在单元测试中导航控制器存在问题。     当我尝试使用我的模拟控制器通过导航堆栈(pushViewController(ViewController,animated :))推送详细视图控制器时,在测试推送功能中无法执行(仅在navigationController初始化时执行)。 在模拟iPhone上,应用程序可以正常运行。     在代码中,mockNavigationController具有pushVC值,当pushViewController执行时该值会更改。     当用户点击单元格时,dataProvider(tableCell的委托和dataSource)将通知发布到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)

在错误的地方需要代码帮助。感谢您的阅读。

1 个答案:

答案 0 :(得分:0)

@Alexander,欢迎来到StackOverflow。

您说dataProvider是被测视图控制器中.dataSource的{​​{1}}和.delegate,它是负责启动导航的人。 / p>

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

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

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

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

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

  • 我建议使用NavigationDelegate pattern测试此行为。通过消除使用MockNavigationController的麻烦,这将使您变得更简单。
  • 最好在测试中使用UIApplication_ = sut.view触发视图控制器视图的设置