你如何测试UIStoryBoard segue是由didSelectRow(at :)触发的

问题描述 投票:1回答:2

所以我有一个带UITableView的故事板。有一个原型单元格,show segue连接到另一个UIViewController

例子enter image description here

  • 单元标识符为“CellOne”
  • segue没有标识符
  • 我的类是tableView的dataSource和delegate。

class看起来像这样:

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准备segue来捕获目标ViewController以及我需要的任何其他东西但是以编程方式调用tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)并不会触发准备segue的代码。

是否有可能测试选择Cell One触发故事板segue而不添加segue标识符并从prepareForSegue中明确调用它?

ios swift unit-testing
2个回答
1
投票

如果您的uitableViewCell是唯一触发到目的地的segue的东西,您可以使用或者:

if let destination = segue.destination as? MyViewController,
   let indexPath = tableView.indexPathForSelectedCell {
     destination.detail = model[indexPath.row]
}

否则,如果您需要消除歧义,可以使用is或as检查发件人的类别


0
投票

更新:长话短说没有很好的方法来做到这一点。让这很困难的是,虽然我们习惯于测试的大多数控件都有动作和发送者,但UITableViewCell上的触摸是一种不同的范例。

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

一种方法是获取对单元格的引用并调用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)
    }
}

另一种(完全矫枉过正的)方式是拥有一个自定义单元格,您可以在其中处理所有自己的触摸逻辑。这将是疯狂的,但这是一种可能性,它将开辟更多的测试选择。我不打算这样表明,因为这样做是一种疯狂的方式。

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

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)
    }

}

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

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的另一种方法是使用类似于视图模型 - 协调器模式的东西来创建和测试viewModel。基本上,您会告诉您的协调员选择了一个单元格,协调员将使用所需的segue标识符更新视图模型。通过这种方式,您可以测试您的协调器对象,并且如果协调器已连接,您可以确定您将触发正确的segue。一个简单的手动测试会告诉你。

在伪代码中:

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选择一个单元格,并确保按预期使用该协调器的模拟实现。您一次测试的较小单元越容易。

© www.soinside.com 2019 - 2024. All rights reserved.