所以我有一个带UITableView的故事板。有一个原型单元格,其中show segue连接到另一个UIViewController
类看起来像这样:
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")
}
}
通常情况下,我会通过调整为segue准备捕获目标ViewController以及我需要的任何其他东西来测试它,但是以编程方式调用tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
不会触发准备segue的代码。
是否可以测试选择Cell One是否触发故事板segue而不添加segue标识符并从prepareForSegue中明确调用它?
答案 0 :(得分:1)
如果您的tableViewCell是唯一触发到目的地的segue的东西,您可以使用 或作为:
if let destination = segue.destination as? MyViewController,
let indexPath = tableView.indexPathForSelectedCell {
destination.detail = model[indexPath.row]
}
否则,如果您需要消除歧义,可以使用 或
检查发件人的类别答案 1 :(得分: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选择一个单元格,并确保按预期使用该协调器的模拟实现。您一次测试的较小单位就越容易。
答案 2 :(得分:0)
仅用于测试segue即可:
在您的VC中:
open let segueToSomewhere = "segueToSomewhere"
open var calledSegue: UIStoryboardSegue!
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
calledSegue = segue
}
在您的测试中:
func testYourSegue() {
//Given
let storyboard = UIStoryboard(name: "Your_storyboard", bundle: nil)
let yourVC = storyboard.instantiateViewController(withIdentifier: "YourVC") as? YourVC
//When
yourVC.performSegue(withIdentifier: previousVC.segueToSomewhere, sender: nil)
//Then
XCTAssertEqual(previousVC.calledSegue.identifier, yourVC.segueToSomewhere, "The selected segue should be \(previousVC.segueToSomewhere)")
}