Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Unit testing a view controller
// From https://albertodebortoli.com/2018/03/12/easy-view-controller-unit-testing/
import XCTest
import UIKit
class ControllerLifecycle<T: UIViewController>
{
private lazy var this = type(of: self).self
private var rootWindow: UIWindow?
var rootController: T? {
return rootWindow?.rootViewController as? T
}
func setupWindow(storyboardName: String, controllerIdentifier: String)
{
let bundle = Bundle(for: T.self)
guard let storyboard = this.createStoryboard(name: storyboardName, bundle: bundle),
let controller = this.createController(storyboard: storyboard, identifier: controllerIdentifier)
else {
return
}
setupWindow(withViewController: controller)
}
func setupWindow(withViewController viewController: T)
{
rootWindow = UIWindow(frame: UIScreen.main.bounds)
guard let rootWindow = rootWindow else { return }
rootWindow.isHidden = false
rootWindow.rootViewController = viewController
_ = viewController.view
viewController.viewWillAppear(false)
viewController.viewDidAppear(false)
}
func tearDownWindow() {
guard let rootWindow = rootWindow, let rootViewController = rootWindow.rootViewController as? T else {
XCTFail("Expected to find a window with a root view controller. However: rootWindow = \(String(describing: self.rootWindow)), rootViewController = \(String(describing: self.rootWindow?.rootViewController)).")
return
}
rootViewController.viewWillDisappear(false)
rootViewController.viewDidDisappear(false)
rootWindow.rootViewController = nil
rootWindow.isHidden = true
self.rootWindow = nil
}
/// Return a storyboard for the given name and bundle. Log a console error if not found.
private static func createController(storyboard: UIStoryboard, identifier: String) -> T? {
var controller: T? = nil
do {
try ObjC.catchException {
controller = storyboard.instantiateViewController(withIdentifier: identifier) as? T
}
} catch {
print("🚨 Storyboard \(storyboard) doesn't contain a view controller with identifier ‘\(identifier)’.")
}
return controller
}
/// Return a storyboard for the given name and bundle. Log a console error if not found.
private static func createStoryboard(name: String, bundle: Bundle) -> UIStoryboard? {
var storyboard: UIStoryboard? = nil
do {
try ObjC.catchException {
storyboard = UIStoryboard(name: name, bundle: bundle)
}
} catch {
print("🚨 Failed to create storyboard \(name): \(error)")
}
return storyboard
}
}
import XCTest
import UIKit
@testable import Foobar
class ViewControllerTests: XCTestCase
{
private typealias Controller = ViewController
private lazy var this = type(of: self).self
private var controllerLifecycle: ControllerLifecycle<Controller>?
override func setUp() {
super.setUp()
controllerLifecycle = ControllerLifecycle<Controller>()
controllerLifecycle?.setupWindow(storyboardName: "Main", controllerIdentifier: "ViewController")
if let controller = controllerLifecycle?.rootController {
print("Stub \(controller)")
}
}
func testFoo(){
print("this is here to trigger setUp() and tearDown()")
}
override func tearDown() {
controllerLifecycle?.tearDownWindow()
controllerLifecycle = nil
super.tearDown()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment