In Unit Test, I have a simple table view with a bunch of basic cells (cells with just a label). I would like to access the cell using `cellForRow(at:)`, so I can test thing like selecting and deselecting the row programmatically, but this `cellForRow` queiry always returns `nil`.
There is some discussion online that I should be using the data source's `tableView(_, cellForRowAt:)` instead. This is _not_ my intention. I only want to test the cell's visibility, test selecting and deselecting them. To test for visibility, `cellForRow(at:)` is the right function to use. Furthermore, the data source's `tableView(_, cellForRowAt:)` has no safeguard for out-of-range index access, while table view's `cellForRow(at:)` will gracefully return `nil` in this case, which I also wanted to test my table view controller on.
However, while I can always get a valid cell from `tableViewController.tableView(_:cellForRowAt:)`, I couldn't understand why I always get `nil` from `tableView.cellForRow(at:)`. I have verfied both the `tableView` and the `tableViewController` are not `nil` in my unit test, and I have also triggered view loading with:
_ = tableViewController.view
in `setUp()`. I also verified with `tableView.indexPathsForVisibleRows`, and the result _does_ include the index path I used for `cellForRow(at:)`.
When I queried my cell through LLDB and breakpoints, sometimes my cell would show up properly. Is it possible that I am missing things like asynchronous waiting since loading up cells visually may be done in a different thread? Am I supposed to add expectation waiting of some sort to wait until the cells are fully loaded up before I can access them with `cellForRow(at:)`, even through `tableView.indexPathsForVisibleRows` already returns the expected index paths.? I tried to set this up but I'm not sure how to override my table view controller's `init()`.
Here's my code in the Unit Test class.
import XCTest
@testable import TableViewTest
class TableViewTestTests: XCTestCase {
private var appDelegate: AppDelegate!
private var tableVC: TableViewController!
override func setUp() {
super.setUp()
appDelegate = UIApplication.shared.delegate as! AppDelegate
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
tableVC = storyboard.instantiateViewController(withIdentifier: "TableViewController") as! TableViewController
// Trigger view load and viewDidLoad()
_ = tableVC.view
}
override func tearDown() {
super.tearDown()
}
func testGetFirstRow() {
let tableView = tableVC.tableView!
let indexPath0 = IndexPath(item: 0, section: 0)
let cell0 = tableView.cellForRow(at: indexPath0)
let visibleRows = tableView.indexPathsForVisibleRows
XCTAssert(visibleRows != nil) // PASSED
XCTAssert(tableView.indexPathsForVisibleRows!.contains(indexPath0)) // PASSED
XCTAssert(cell0 != nil) // FAILED
}
func testGetFirstRowDataSource() {
let tableView = tableVC.tableView!
let indexPath0 = IndexPath(item: 0, section: 0)
// This won't check for cell visibility.
let cell0 = tableVC.tableView(tableView, cellForRowAt: indexPath0)
let visibleRows = tableView.indexPathsForVisibleRows
XCTAssert(visibleRows != nil) // PASSED
XCTAssert(tableView.indexPathsForVisibleRows!.contains(indexPath0)) // PASSED
}