测试自定义UITableViewCell,cellForRowAtIndexPath与nil出口崩溃

时间:2015-06-14 17:15:28

标签: swift uitableview

我有一个包含tableView的ViewController。由于我需要保持代码完全覆盖测试,我需要为[tableView:cellForRowAtIndexPath]编写测试

import UIKit

class MainViewController: UIViewController, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    var source = [
        Country(name: "USA", capital: "Washington, D.C."),
        Country(name: "Argentina", capital: "Buenos Aires"),
        Country(name: "Mexico", capital: "Mexico, D.F.")
    ]
    let cellIdentifier = NSStringFromClass(MainViewController.self)

    init() {
        super.init(nibName: "MainViewController", bundle: nil)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let nib = UINib(nibName: "CustomCell", bundle: nil)
        tableView?.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
        tableView.dataSource = self
        }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return source.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? CustomCell
        if let cell = cell {
            cell.countryLabel.text = source[indexPath.row].name
            cell.capitalLabel.text = source[indexPath.row].capital
            return cell
        }
        return CustomCell()
    }
}

struct Country {
    var name: String
    var capital: String
}

测试如下所示:

import UIKit
import XCTest

class MainViewControllerTests: XCTestCase {

    var controller: MainViewController!

    override func setUp() {
        super.setUp()

        controller = MainViewController()
        controller.view.description
    }

    override func tearDown() {
        super.tearDown()
    }

    func testTableViewOutlet() {
        XCTAssertNotNil(controller.tableView)
    }

    func testTableViewCellForRowAtIndexPath() {
        let indexPath = NSIndexPath(forRow: 0, inSection: 0)

        let cell = controller.tableView(controller.tableView, cellForRowAtIndexPath: indexPath) as! CustomCell

        XCTAssertEqual(cell.countryLabel.text!, "USA")
        XCTAssertEqual(cell.capitalLabel.text!, "Washington, D.C.")
    }
}

运行测试时出现以下错误:

Test Case '-[CustomTableViewCellTests.MainViewControllerTests testTableViewCellForRowAtIndexPath]' started.
fatal error: unexpectedly found nil while unwrapping an Optional value

由于[dequeueReusableCellWithIithntifier]仅在从测试运行时返回nil,因此此函数在我的生产应用程序中提到了几个月的测试。我实际上尝试了很多我在StackOverflow中读过的方法,例如:

  1. [dequeueReusableCellWithIdentifier]返回nil时创建CustomCell
  2. 使用temporaryController加载CustomCell的NIB并抓取应该连接到单元格的视图
  3. 底线是我接近解决方案,我创建了一个CustomCell对象,但是单元格的出口仍为零。我还没有找到能够实现初始化元素出口的代码片段,因此我可以通过测试。

    我包含一个基本的iOS项目,它有自己的git,其中包含我在此处包含的代码。它是一个UIViewController,包含UITableView,它使用自定义UITableViewCell显示数据。我希望找到一个解决方案,通过其出口实现自定义单元以使测试通过。

    Git回购来源:     https://bitbucket.org/jallauca/customcell/src

    克隆git repo:

    git clone https://bitbucket.org/jallauca/customcell.git 
    

4 个答案:

答案 0 :(得分:3)

对我来说,(Swift 3)我不得不调用cellForRow数据源cellForRowAt函数而不是直接从tableView.cellForRow函数访问

//this 
let cell = controller.tableView(controller.tableView, cellForRowAt: IndexPath(row: 0, section: 0))

//instead of this
let cell = controller.tableView.cellForRow(at: IndexPath(row: 0, section: 0))

答案 1 :(得分:0)

这里有很多可能出错的事情。一些可能性:

  1. tableView本身可能是零。
  2. 在具有重用标识符的笔尖或故事板中没有单元格原型。
  3. 您尚未注册具有相关重用标识符的单元格类。
  4. 由于某些其他原因(例如内存不足),单元格分配失败。
  5. 某些类,例如表格单元格类,未包含在测试目标中。 (这通常会导致构建错误而不是崩溃。)
  6. Cell的.xib文件未包含在测试目标中。
  7. .xib中的相关对象未连接到正确的插座。
  8. 您的崩溃正在发生,因为您尝试在tableView:cellForRowAtIndexPath:方法中分配新单元格。如果没有可用于重用的单元,那么过去是必要的,但是现在正确配置的表将根据需要实例化新单元,而IIRC实例化自己的单元可能会导致崩溃。

答案 2 :(得分:0)

有两个单独的概念,tableView和dataSource。 TableView只是一个视图,并且具有作为视图的单元格。 DataSource是tableView的数据提供程序,并且是唯一负责数据的组件。 在测试示例中,您调用tableView.cellForRowAtIndexPath函数以获取特定indexPath中的单元格。根据官方文档, 如果该单元格不可见,此函数将返回nil 。因此,仅当您知道表格可见时,才应使用此功能。 在您的示例中,您试图测试您的表将正确加载数据。引用数据时,首先要想到的是数据源。因此,您需要测试dataSource将正确的信息绑定到您的单元格。结果,您致电controller.tableView.dataSource(controller.tableView, cellForRowAt: IndexPath(row: 0, section: 0)))

答案 3 :(得分:0)

步骤1:在情节提要中转到视图控制器的Identity Inspector,并为其指定一个情节提要ID。例如:“ vcIdentifier”

第2步:您已经在单元测试中获得了情节提要的实例

第2步:通过第2步中的情节提要实例实例化视图控制器

第3步:从viewcontroller中访问表视图,然后将其传递给测试中的'cellForRowAt'方法。请参见下面的示例代码:

    let storyboard = UIStoryboard(name: "Main", bundle:nil)
    let newsViewController: UITableViewController = storyboard.instantiateViewController(identifier: "vcIdentifier")
    return newsViewController.tableView