你如何测试 UIStoryBoard segue 是由 didSelectRow(at:) 触发的 [英] How do you test UIStoryBoard segue is triggered by didSelectRow(at:)

查看:22
本文介绍了你如何测试 UIStoryBoard segue 是由 didSelectRow(at:) 触发的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有一个带有 UITableView 的故事板.有一个原型单元,带有连接到另一个 UIViewController 的显示转场

So I have a storyboard with a UITableView. There is a prototype cell with a show segue hooked up to another UIViewController

示例

  • 单元标识符为CellOne"
  • segues 没有标识符
  • 我的类是 tableView 的数据源和委托.

类看起来像这样:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var tableView: UITableView!

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return tableView.dequeueReusableCell(withIdentifier: "CellOne", for: indexPath)
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Selected the row")
    }
}

通常我会通过 swizzling prepare for segue 来捕获目标 ViewController 以及我需要的任何其他东西来测试它,但以编程方式调用 tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 不会触发准备转场的代码.

Normally I would test it by swizzling prepare for segue to capture the destination ViewController and whatever else I need but calling tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) programmatically doesn't trigger the code in prepare for segue.

是否可以在不添加 segue 标识符并从 prepareForSegue 中显式调用它的情况下测试选择 Cell One 会触发故事板 segue?

Is it possible to test that selecting Cell One triggers the storyboard segue without adding a segue identifier and calling it explicitly from prepareForSegue?

推荐答案

更新:长话短说,没有什么好方法可以做到这一点.使这变得困难的是,虽然我们习惯于测试的大多数控件都有一个动作和一个发送者,但对 UITableViewCell 的触摸是一种不同的范例.

Updated: Long story short there isn't a great way to do this. What makes this difficult is that while most controls we're used to testing have an action and a sender, a touch on a UITableViewCell is a different paradigm.

也就是说,拥有 segue 标识符基本上是任何策略的先决条件.

That said, having a segue identifier is basically a pre-requisite for any strategy.

一种方法是获取对单元格的引用并调用 performSegue(withIdentifier:,sender:):

One way is to get a reference to the cell and call performSegue(withIdentifier:,sender:):

class ViewControllerTests: XCTestCase {

    func testClickingACell() {
        let controller = UIStoryboard(name: "ViewController", bundle: nil).instantiateInitialViewController() as! ViewController
        let cell = controller.tableView.dataSource?.tableView(controller.tableView, cellForRowAt: IndexPath(row: 0, section: 0))

        controller.performSegue(withIdentifier: "MySegue", sender: cell)

        XCTAssertNotNil(controller.presentedViewController as? TheDestinationViewController)
    }
}

另一种(完全矫枉过正)方法是拥有一个自定义单元格,您可以在其中处理您自己的所有触摸逻辑.这会很疯狂,但这是一种可能性,它会为测试提供更多选择.我不会以这种方式展示,因为这将是一种疯狂的方式.

Another (completely overkill) way would be to have a custom cell where you handle all of your own touch logic. This would be insane but it's a possibility and it would open up more options for testing. I'm not going to show this way because it would be an insane way to do it.

另一种方法是使用不同的架构,摆脱 UIKit 并只允许测试 performSegue 逻辑.示例:

Another way is to use a different architecture that gets rid of UIKit and allows for testing just the performSegue logic. Example:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet var myTableView: UITableView!
    var navigator: UIViewController?

    override func viewDidLoad() {
        super.viewDidLoad()

        navigator = self
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        navigator?.performSegue(withIdentifier: "MySegue", sender: nil)
    }

}

这允许您在测试中执行以下操作:

This allows you to do something like this in your tests:

class MockNavigator: ViewController {
    var performSegueCalled = false
    var performSegueIdentifier: String?

    override func performSegue(withIdentifier identifier: String, sender: Any?) {
        performSegueCalled = true
        performSegueIdentifier = identifier
    }
}

func testExample() {
    let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as! ViewController
    controller.loadViewIfNeeded()

    // Need to keep a reference to be able to assert against it
    let mockNavigator = MockNavigator()

    controller.navigator = mockNavigator

    controller.tableView(controller.myTableView, didSelectRowAt: IndexPath(row: 0, section: 0))

    XCTAssertTrue(mockNavigator.performSegueCalled)
    XCTAssertEqual(mockNavigator.performSegueIdentifier, "MySegue")
}

另一种构建代码以避免 UIKit 的方法是使用视图模型协调器模式之类的东西来创建和测试视图模型.基本上,您会告诉您的协调器选择了一个单元格,并且协调器将使用所需的 segue 标识符更新视图模型.通过这种方式,您可以测试您的协调器对象,并确保如果协调器已连接,您将触发正确的转场.一个简单的手动测试就会告诉您这一点.

Another way to structure your code to avoid UIKit is to use something like a view model-coordinator pattern to create and test a viewModel. Basically you'd tell your coordinator that a cell was selected and the coordinator would update a view model with the desired segue identifier. This way you could test your coordinator object to and be mostly sure that you'll trigger the correct segue if the coordinator is hooked up. A simple manual test would tell you that.

在伪代码中:

struct ViewModel {
    let labelText: String
    let segueIdentifier: String
}

class Coordinator {
    var data = [YourObject]()
    var viewModel = ViewModel(labelText: "", segueIdentifier: "")

    func selectedItem(at row: Int) {
        let item = data[row]

        // Do some logic to figure out which identifier you want
        var segueIdentifer: String
        if item == whatever {
            segueIdentifier = "something"
        }
        viewModel = ViewModel(labelText: item.text, segueIdentifier: segueIdentifier)
    }
}

可能最好的方法是方法的组合.将协调器与经过单独测试的视图模型一起使用.然后进行一个测试,您使用 UIKit 选择一个单元格,并确保按预期使用该协调器的模拟实现.一次测试的单元越小,就越容易.

Probably the best way is a combination of approaches. Use a coordinator with a view model that's tested on its own. Then have a test where you use UIKit to select a cell and make sure that a mocked implementation of that coordinator is used as expected. The smaller units you're testing at a time the easier it will be.

这篇关于你如何测试 UIStoryBoard segue 是由 didSelectRow(at:) 触发的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆