Home > Software engineering >  Testing tableview.reloadData()
Testing tableview.reloadData()

Time:09-29

while using a MockTableView this code still not calling reloadData() from the mock, please i wanna know what is wrong here.

following this book: Test-Driven IOS Development with Swift 4 - Third Edition

page 164, i was as an exercise

full code repo - on github

ItemListViewController.swift

import UIKit

class ItemListViewController: UIViewController, ItemManagerSettable {

    @IBOutlet var tableView: UITableView!
    @IBOutlet var dataProvider: (UITableViewDataSource & UITableViewDelegate &
                                 ItemManagerSettable)!
    var itemManager: ItemManager?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        itemManager = ItemManager()
        dataProvider.itemManager = itemManager
        tableView.dataSource = dataProvider
        tableView.delegate = dataProvider
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        tableView.reloadData()
    }
    @IBAction func addItem(_ sender: UIBarButtonItem) {
        if let nextViewController =
            storyboard?.instantiateViewController(
                withIdentifier: "InputViewController")
            as? InputViewController {
            nextViewController.itemManager = itemManager
            
            present(nextViewController, animated: true, completion: nil)
        }
    }
}

ItemListViewControllerTest.swift

import XCTest

 @testable import ToDo

 class ItemListViewControllerTest: XCTestCase {

    var sut: ItemListViewController!
    var addButton: UIBarButtonItem!
    var action: Selector!
    
    override func setUpWithError() throws {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: 
         "ItemListViewController")
        sut = vc as? ItemListViewController
        addButton = sut.navigationItem.rightBarButtonItem
        action = addButton.action
        UIApplication.shared.keyWindow?.rootViewController = sut
        
        sut.loadViewIfNeeded()
        
    }
    override func tearDownWithError() throws {}

    func testItemListVC_ReloadTableViewWhenAddNewTodoItem() {
        
        let mockTableView = MocktableView()
        sut.tableView = mockTableView
        
        guard let addButton = sut.navigationItem.rightBarButtonItem else{
            XCTFail()
            return
        }
        
        guard let action = addButton.action else{
            XCTFail()
            return
        }
        sut.performSelector(onMainThread: action, with: addButton, waitUntilDone: true)
        
        guard let inputViewController = sut.presentedViewController as? 
         InputViewController else{
            XCTFail()
            return
        }
        inputViewController.titleTextField.text = "Test Title"
        inputViewController.save()

        XCTAssertTrue(mockTableView.calledReloadData)
    }
} 

extension ItemListViewControllerTest{
    
    class MocktableView: UITableView{
        
        var calledReloadData: Bool = false
        
        override func reloadData() {
            calledReloadData = true
            super.reloadData()

        }
        
    }
}

CodePudding user response:

You inject a MockTableview Then you call loadViewIfNeeded(). But because this view controller is storyboard-based and the table view is an outlet, the actual table view is loaded at this time. This replaces your MockTableview.

One solution is:

  1. Call loadViewIfNeeded() first
  2. Inject the MockTableview to replace the actual table view
  3. Call viewDidLoad() directly. Even though loadViewIfNeeded() already called it, we need to repeat it now that we have a different tableview in place.

Another possible solution:

  • Avoid MockTableview completely. Continue to use a real table view. You can test whether it reloads data by checking whether the number of rows matches the changed data.

Yet another solution:

  • Avoid storyboards. You can do this with plain XIBs (but these lack table view prototype cells) or programmatically.

By the way, I see all your tearDownWithError() implementations are empty. Be sure to tear down everything you set up. Otherwise you will end up with multiple instances of your system under test alive at the same time. I explain there here: https://qualitycoding.org/xctestcase-teardown/

  • Related