当触发动作时,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
和 .delegate
的 UITableView
在你的测试视图控制器中,它负责启动导航.
您确定在测试中 dataProvider
实际上设置为 .dataSource
和 .delegate
吗?如果不是这种情况,则永远不会调用启动导航的代码。
您可以使用断点来验证两件事:
- 如果你的
showDetails
方法被调用
- 如果调用
MockNavigationController
中的 pushViewController(_:, animated:)
方法
我猜其中一个未被调用,这可能会指出问题的原因。
如果允许的话,多说几句:
- 我建议使用
NavigationDelegate
pattern 来测试此行为。通过删除 fiddle 和 UIApplication
. 的需要,它会让你更简单
- 在测试中最好使用
_ = sut.view
或sut.beginAppearanceTransition(true, animated: false)
来触发视图控制器视图的设置
我正在学习 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
和 .delegate
的 UITableView
在你的测试视图控制器中,它负责启动导航.
您确定在测试中 dataProvider
实际上设置为 .dataSource
和 .delegate
吗?如果不是这种情况,则永远不会调用启动导航的代码。
您可以使用断点来验证两件事:
- 如果你的
showDetails
方法被调用 - 如果调用
MockNavigationController
中的pushViewController(_:, animated:)
方法
我猜其中一个未被调用,这可能会指出问题的原因。
如果允许的话,多说几句:
- 我建议使用
NavigationDelegate
pattern 来测试此行为。通过删除 fiddle 和UIApplication
. 的需要,它会让你更简单
- 在测试中最好使用
_ = sut.view
或sut.beginAppearanceTransition(true, animated: false)
来触发视图控制器视图的设置