测试自定义 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