Reputation: 500
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?
Upvotes: 2
Views: 4971
Reputation: 32923
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)
}
Upvotes: 1
Reputation: 52108
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)
}
Upvotes: 7