简体   繁体   中英

How to unit test `viewDidLoad()`

What is everybody's favourite way of injecting dependencies when unit testing UIViewController.viewDidLoad() method on iOS?

Given my implementation is:

class MyViewController: UIViewController {

    var service: Service = Service()

    override func viewDidLoad() {
        super.viewDidLoad()
        service.load()
    }
}

And my test class is something along this:

class MyViewController Tests: XCTestCase {

     var vc: MyViewController!
     var serviceMock = ServiceMock()

    override func setUp() {
       super.setUp()
       let storyboard = UIStoryboard(name: "Main", bundle: nil)
       vc = storyboard.instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
       vc.service = serviceMock
    }
}

func testThatServiceIsCalled() {

       XCTAssertTrue(serviceMock.loadCalled)
 }

The obvious problem here is that viewDidLoad() is called at the moment I instantiate the viewController and the test fails as the mock is not injected properly.

Any solution?

Why do you need to use the storyboard to create the view controller?

Wouldn't this work?

func testThatServiceIsCalled() {
  let vc = MyViewController()
  vc.service = serviceMock
  vc.viewDidLoad()

  XCTAssertTrue(serviceMock.loadCalled)
}

TL;DR You should not unit test UI-related methods like this.

Controllers and Views are not suitable for unit testing for the simple reason that their usually involves operations that get reflected on the screen, and thus are (very) hard to test. Snapshot testing or UI automation tests are a better candidate for these two components of MVC.

A well-written Controller doesn't contain business logic, it delegates the UI stuff to the View, and the business actions to its Model. With this in mind, it's the Model that should be the one that gets to be unit tested.

Now, if you really want to assert that the your service get's called with the proper methods, you could set your VC as root view controller, this should ensure the loadView method gets called, which should trigger the viewDidLoad one:

func testThatServiceIsCalled() {
    let window = UIWindow(frame:  UIScreen.main.bounds)
    window.makeKeyAndVisible()
    window.rootViewController = vc
    XCTAssertTrue(serviceMock.loadCalled)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM