I am writing unit tests for my UITableViewController's data source, which has a UISearchController for filtering results. I need to test the logic in NumberOfRowsInSection so that when the search controller is active, the data source returns the count from the filtered array rather than the normal array.
The function controls this by checking if the search controller's 'isActive' is true/false.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchController.isActive ? filteredResults.count : results.count
}
So my unit test is written like this
func testNumberOfRowsInSection() {
XCTAssertFalse(searchController.isActive)
XCTAssertEqual(4, dataSource.tableView(tableView, numberOfRowsInSection: 0))
searchController.isActive = true
XCTAssertTrue(searchController.isActive) // Fails right after setting it true
XCTAssertEqual(0, dataSource.tableView(tableView, numberOfRowsInSection: 0)) // Fails
searchController.isActive = false
XCTAssertFalse(searchController.isActive)
XCTAssertEqual(4, dataSource.tableView(tableView, numberOfRowsInSection: 0))
}
So the 'isActive' property is not staying true immediately after setting it to true in unit tests. This is strange because during the regular run of the app I can set it to true in the view controller and it stays active.
override func viewDidLoad() {
super.viewDidLoad()
// ...
searchController.isActive = true // Makes search bar immediately visible
// ...
}
The documentation shows that it's a settable property, so why doesn't setting it to true do anything in unit tests? I've also tried attaching it to a navigation bar but that didn't change anything. If I can't test it like this in unit tests, I'm going to have to mock that functionality, which would be annoying since it should be simple to test this.
Updated example:
class MovieSearchDataSourceTests: XCTestCase {
private var window: UIWindow!
private var controller: UITableViewController!
private var searchController: UISearchController!
private var sut: MovieSearchDataSource!
override func setUp() {
window = UIWindow()
controller = UITableViewController()
searchController = UISearchController()
sut = MovieSearchDataSource(tableView: controller.tableView,
searchController: searchController,
movies: Array(repeating: Movie.test, count: 4))
window.rootViewController = UINavigationController(rootViewController: controller)
controller.navigationItem.searchController = searchController
}
override func tearDown() {
window = nil
controller = nil
searchController = nil
sut = nil
RunLoop.current.run(until: Date())
}
func testNumberOfRowsInSection() {
window.addSubview(controller.tableView)
controller.loadViewIfNeeded()
XCTAssertFalse(searchController.isActive)
XCTAssertEqual(4, sut.tableView(controller.tableView, numberOfRowsInSection: 0))
searchController.isActive = true
XCTAssertTrue(searchController.isActive)
XCTAssertEqual(0, sut.tableView(controller.tableView, numberOfRowsInSection: 0))
searchController.isActive = false
XCTAssertFalse(searchController.isActive)
XCTAssertEqual(4, sut.tableView(controller.tableView, numberOfRowsInSection: 0))
}
}