Search code examples
swiftunit-testingxctestxctestcase

Testing method in UITabBarController sets title correctly


I am currently trying to understand Unit Testing in Swift 4. I have a class, with a method that setups my view controllers.

I would like to ensure that this method sets the title on the ViewController correctly.

However I cannot understand how to write this test?

This is my code and test so far.

Currently my tests fails with:

XCTAssertEqual failed: ("nil") is not equal to ("Optional("Favourites")") - 

How is it possible to test this behaviour? Any help would be much appreciated.

Controller

class MainTabBarController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        setupTabBar()
        setupViewControllers()
    }

    // MARK:- Setup

    fileprivate func setupViewControllers() {
        viewControllers = [
            generateNavigationController(with: UIViewController(), title: "Favourites", image: UIImage(imageLiteralResourceName: "favorites")),
            generateNavigationController(with: UIViewController(), title: "Search", image: UIImage(imageLiteralResourceName: "search")),
            generateNavigationController(with: UIViewController(), title: "Downloads", image: UIImage(imageLiteralResourceName: "downloads"))
        ]
    }

    fileprivate func setupTabBar() {
        tabBar.tintColor = .purple
    }

    // MARK:- Helpers

    fileprivate func generateNavigationController(with rootViewController: UIViewController, title: String, image: UIImage) -> UIViewController {

        let controller = UINavigationController(rootViewController: rootViewController)

        controller.tabBarItem.title = title
        controller.tabBarItem.image = image

        rootViewController.navigationItem.title = title
        navigationController?.navigationBar.prefersLargeTitles = true

        return controller
    }
}

Tests

import Foundation
import XCTest

@testable import Podcasts

class MainTabBarControllerTests: XCTestCase {

    func testTheInitialViewControllerShouldHaveTitleFoo() {
        let sut = MainTabBarController()

       let _ = sut.viewDidLoad()

        XCTAssertEqual(sut.navigationItem.title, "Favourites")
    }
}

Solution

  • There are some issues with accessing proper views in your code snippet. Btw I wrote an additional test for your tab bar controller, hope it will help :) Try this to pass the test:

    class MainTabBarControllerTests: XCTestCase {
    
        func testFirstTabTitleIsCorrectAfterInitialSetup() {
            // Given
            let sut = MainTabBarController()
    
            // When
            sut.viewDidLoad()
    
            // Then
            let viewController = sut.viewControllers?.first
            let title = viewController?.tabBarItem.title
            XCTAssertEqual(title, "Favourites")
        }
    
        func testNavigationTitleIsCorrectAfterInitialSetup() {
            // Given
            let sut = MainTabBarController()
    
            // When
            sut.viewDidLoad()
    
            // Then
            let viewController = sut.viewControllers?.first as? UINavigationController
            let title = viewController?.viewControllers.first?.navigationItem.title
            XCTAssertEqual(title, "Favourites")
        }
    }
    

    It's good that you've already used Given-When-Then, but it would even great if you improve naming of the test. There are a lot of conventions, for example, I prefer something like test_SubjectUnderTest_doSomething_whenConditionsAreCorrect.

    Here is another tip. Try to figure out how to separate the logic between view and view controller. It you get your hands dirty into at least MVP (Model-View-Presenter), then you'll figure out that its testability is better.

    By the way, instead of this kind of tests it's more reasonable to consider UI tests. UI tests mostly rely on accessibility IDs. The most popular tools for test automation are Appium or XCUITests/Earlgrey if you prefer to go with native.