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

Posted

技术标签:

【中文标题】测试自定义 UITableViewCell,cellForRowAtIndexPath 与 nil 出口崩溃【英文标题】:Tests for custom UITableViewCell, cellForRowAtIndexPath crashes with nil outlets 【发布时间】:2015-06-14 17:15:27 【问题描述】:

我有一个包含 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

这个函数已经在我的生产应用程序中暗示了几个月的测试,因为 [dequeueReusableCellWithIdentifier] 仅在从测试运行时返回 nil。实际上,我已经尝试了许多在 *** 中阅读过的不同方法,例如:

    当 [dequeueReusableCellWithIdentifier] 返回 nil 时创建 CustomCell 使用临时控制器为 CustomCell 加载 NIB 并抓取应该连接到单元格的视图

底线是我接近解决方案,我创建了一个 CustomCell 对象,但单元格的出口仍然为零。我还没有找到将插座实现为初始化元素的代码,因此我可以通过测试。

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

Git 代码库来源: https://bitbucket.org/jallauca/customcell/src

克隆 git 仓库:

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

【问题讨论】:

每当您提出有关错误或崩溃的问题时,还请包括解释崩溃的任何日志消息,也许还包括堆栈跟踪的相关部分。 我的网络浏览器警告我您的链接指向可能的钓鱼网站。请直接发布相关代码,而不是要求人们在其他地方查看。 我的第一个猜测是视图没有加载。仅从 xib/storyboard 实例化控制器不会加载整个视图或连接所有插座。访问视图属性可能会完成这项工作,尽管以这种方式进行测试会留下许多问题 - 因为许多行为可能只是未定义且没有意义,例如查询从未成为视图树一部分的元素的 UI 状态。跨度> bitbucket.org 是一个免费的托管网站,就像github.com 一样。由于 iOS 应用程序会生成与故障排除相关的代码和 xib 文件,因此更容易克隆项目或仅查看源代码(如果它回答了问题)。 关于 SO 的问题旨在对未来的读者有用,而不仅仅是对最初提出问题的人有用。链接总是中断,因此在问题中包含代码很重要。您的原始链接包含您的用户 ID,这会导致某些浏览器警告网络钓鱼。我编辑了问题以更正链接以避免问题,但在问题中包含代码仍然很重要。 【参考方案1】:

对我来说,(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))

【讨论】:

【参考方案2】:

这里有很多可能出错的地方。一些可能性:

    tableView 本身可能为零。 笔尖或情节提要中没有单元原型与重用标识符相关。 您尚未使用相关重用标识符注册单元类。 由于其他原因(例如内存不足),单元分配失败。 某些类,例如表格单元类,未包含在测试目标中。 (这通常会导致构建错误而不是崩溃。) Cell 的 .xib 文件未包含在测试目标中。 .xib 中的相关对象未连接到正确的插座。 发生崩溃是因为您试图在tableView:cellForRowAtIndexPath: 方法中分配一个新单元格。如果没有单元格可供重用,这曾经是必要的,但如今,正确配置的表格将根据需要实例化新单元格,而 IIRC 实例化您自己的单元格可能会导致崩溃。

【讨论】:

我为喜欢在论坛中查看代码的开发人员提供了两个主要的源文件。 1. tableView 不是 nil,因为测试现在显示。 2.我用的不是storyboard,而是xib,但是我在viewDidLoad中注册了cell类。我也必须在xib中这样做吗? 3.我确实注册了代码中现在显示的类。 4. 在这种情况下我可以丢弃内存不足。 5.所有类都包含在测试目标中,我仔细检查了。 6. 同 5。 很高兴您能指出代码有什么问题。你能提供我需要做的步骤来修复它吗?具体来说,“.xib 中的相关对象未连接到正确的插座”。我错过了什么? 这只是一个可能的问题。我还没有检查您的 .xib 以验证对象是否已连接到它们的插座。你应该能够自己做到这一点。如果您发现它们没有连接到它们的插座,请连接它们!将上面的列表视为要检查的项目,而不是您的代码肯定有问题的事情。 我已经确认插座连接正确。当应用程序运行时,表格视图、单元格及其各自的出口正确地显示所有数据。测试运行时,tableview 出口已连接,但单元格或其出口不会具体化为对象。如果可以,请下载代码,您会真正看到缺少的东西并不明显。我浏览了所有的 ***,但尽管解决方案是相关的,但与单元测试无关。 @JaimeAllauca,您找到解决问题的方法了吗?目前我遇到了类似的问题【参考方案3】:

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

【讨论】:

【参考方案4】:

第 1 步:转到 Storyboard 中视图控制器的 Identity Inspector 并为其指定一个故事板 ID。例如:“vcIdentifier”

第 2 步:您已经在单元测试中获得了故事板的实例

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

第 3 步:从视图控制器获取对表格视图的访问权,并将其传递给测试中的“cellForRowAt”方法。请看下面的示例代码:

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

【讨论】:

以上是关于测试自定义 UITableViewCell,cellForRowAtIndexPath 与 nil 出口崩溃的主要内容,如果未能解决你的问题,请参考以下文章

自定义 UITableViewCell 的 iOS 单元测试

在 UITableViewCell 中的 detailTextLabel 中添加多行

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

UITableView 接收自定义 UITableViewCell 但没有显示

Swift 自定义 UITableviewCell 宽度问题

将自定义 UITableViewCell 与 ViewCellRenderer 一起使用时出现 NullReferenceException