[iOS、Swift] ユニットテストの時に、任意のタイミングでViewDidLoad()、ViewWill(Did)Appear()、ViewWill(Did)Disappear()を呼び出す方法

  • 19
    Like
  • 2
    Comment

忘れがちなので備忘も兼ねてまとめておきます。

事前準備

以下のようなView Controller(Initial View Controller)を作成します。

HogeViewController.swift

import UIKit

class HogeViewController: UIViewController {

    // ViewDidLoad()用フラグ
    var isViewDidLoadCalled = false

    // ViewWill(Did)Appear()用フラグ
    var isViewWillAppearCalled = false
    var isViewDidAppearCalled = false

    // ViewWill(Did)Disappear()用フラグ
    var isViewWillDisappearCalled = false
    var isViewDidDisappearCalled = false

    // MARK: - View Controller Lifecycle Methods
    override func viewDidLoad() {
        super.viewDidLoad()
        self.isViewDidLoadCalled = true
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.isViewWillAppearCalled = true
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.isViewDidAppearCalled = true
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.isViewWillDisappearCalled = true
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.isViewDidDisappearCalled = true
    }
}

次に、このView Controllerをテストするファイルを作成し、以下のようなセットアップを行います。

HogeViewControllerTests.swift

import XCTest
@testable import InvokeVCLifecycleMethods

class HogeViewControllerTests: XCTestCase {

    var hogeVC: HogeViewController!

    override func setUp() {
        super.setUp()

        let storyboard = UIStoryboard(name: "Main", bundle: nil)

        hogeVC = storyboard.instantiateInitialViewController() as! HogeViewController
    }

    override func tearDown() {
        super.tearDown()
    }

    func test_ViewDidLoad_Called() {
        XCTAssertTrue(hogeVC.isViewDidLoadCalled)
    }

    func test_ViewWillAndDidAppear_Called() {
        XCTAssertTrue(hogeVC.isViewWillAppearCalled)
        XCTAssertTrue(hogeVC.isViewDidAppearCalled)
    }

    func test_ViewWillAndDidDisappear_Called() {
        XCTAssertTrue(hogeVC.isViewWillDisappearCalled)
        XCTAssertTrue(hogeVC.isViewDidDisappearCalled)
    }

}

この状態でテストを実行すると、全てのテストがFailします。

all_failed.png

これで事前準備は完了です。

View Controller LifeCycleメソッドを呼び出す

それでは早速、各種メソッドを呼び出していきましょう。

ViewDidLoad()

ViewDidLoad()は、View Controllerのviewプロパティにアクセスすることで呼び出せます。

[追記] @kazuhiro4949さんより、viewへのアクセスではなくloadViewIfNeeded()ViewDidLoad()を呼び出す方法を教えていただきました。ありがとうございます!

    func test_ViewDidLoad_Called() {
        // ViewDidLoad()を呼び出す
        hogeVC.loadViewIfNeeded()

        XCTAssertTrue(hogeVC.isViewDidLoadCalled)
    }

結果

pass1.png

Passしました。

ViewWill(Did)Appear()

ViewWillAppear()は、UIViewControllerにbeginAppearanceTransition(_:animated:)というインスタンスメソッドが用意されており、その第一引数(_ isAppearing: Bool)をtrueにして呼ぶことで呼び出せます。ViewDidAppear()は、そのインスタンスメソッドを呼んだ後にendAppearanceTransition()を呼ぶことで呼び出せます。

        // ViewWillAppear()を呼び出す(第一引数をtrueにする)
        hogeVC.beginAppearanceTransition(true, animated: false)
        // ViewDidAppear()を呼び出す
        hogeVC.endAppearanceTransition()

結果

pass_appear.png

これもPassしました。

ViewWill(Did)Disappear()

ViewWillDisappear()は、先程のbeginAppearanceTransition(_:animated:)の第一引数(_ isAppearing: Bool)をfalseにして呼ぶことで呼び出せます。ViewDidDisappear()は、その後にendAppearanceTransition()を呼ぶことで呼び出せます。

    func test_ViewWillAndDidDisappear_Called() {
        // ViewWillDisappear()を呼び出す(第一引数をfalseにする)
        hogeVC.beginAppearanceTransition(false, animated: false)
        // ViewDidDisappear()を呼び出す
        hogeVC.endAppearanceTransition()

        XCTAssertTrue(hogeVC.isViewWillDisappearCalled)
        XCTAssertTrue(hogeVC.isViewDidDisappearCalled)
    }

結果

pass_disappear.png

全てPassしました。

ユニットテストを書く時にご活用ください。

参考

Work with View Controllers
https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/WorkWithViewControllers.html)

[追記] loadViewIfNeeded()
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621446-loadviewifneeded

beginAppearanceTransition(_:animated:)
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621387-beginappearancetransition

endAppearanceTransition()
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621503-endappearancetransition

iOS: The One Weird Trick For Testing View Controllers in Swift

Testing in Swift