kikki's tech note

技術ブログです。UnityやSpine、MS、Javaなど技術色々について解説しています。

XCUITestでUIテストを自動化する

本章では、iOSでXCUITestを用いた、UIテストの作成方法について共有します。

XCUITestとは

XCUITestは、XCodeに付属したUI上でテストを実行できるテストフレームワークです。 XCUITestでは、アプリのUI要素に対して、タップ、スワイプ、スクロールといったユーザーの操作を伴った動作確認ができます。
XCUITestにより、UIテストの自動化ができ、特に回帰テスト中に時間を節約できます。 AndroidのEspresso同様、XCode上の操作記録ツールを利用することで、コードの作成なくテストケースの作成もできます。

XCUITestによるUIテストの作成

事前準備

まずは、テスト開始前に常に初期設定となるよう、アプリを削除する処理を準備しておきます。用意の仕方については、以下のサイトを参照しました。 qiita.com

[Springboard.swift]

//
//  Springboard.swift
//

import XCTest
import Foundation

class Springboard: XCTestCase {
    static let shared = Springboard()
    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    
    /**
     Terminate and delete the app via springboard
     */
    func deleteApp() {
        XCUIApplication().terminate()
        
        // Force delete the app from the springboard
        let icon = springboard.icons["【アプリ名】"]
        if icon.exists {
            let iconFrame = icon.frame
            let springboardFrame = springboard.frame
            icon.press(forDuration: 1.3)
            
            // Tap the little "X" button at approximately where it is. The X is not exposed directly
            springboard.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + 3) / springboardFrame.maxX, dy: (iconFrame.minY + 3) / springboardFrame.maxY)).tap()
            
            // Wait for showing alert
            let alert = springboard.alerts["Delete “【アプリ名】”?"]
            let alertExists = NSPredicate(format: "exists == true")
            expectation(for: alertExists, evaluatedWith: alert, handler: nil)
            waitForExpectations(timeout: 5, handler: nil)
            
            springboard.alerts.buttons["Delete"].tap()
            
            XCUIDevice.shared.press(.home)
        }
    }
}

テストで使用する便利関数

XCUITestでも、ユーザー操作による画面上のレスポンスを待機する必要があります。 そこで、ユーザー操作を待ち合わせる、拡張関数を事前に定義しておきます。 [Extension.swift]

//
//  Extension.swift
//

import XCTest
import Foundation

extension XCUIElement {
    func tapIfExists(timeout: Double = 10) {
        if self.waitForExistence(timeout: timeout) {
            self.tap()
        }
    }
    
    func lazyTap(time: uint = 1){
        sleep(time)
        self.tap()
    }
    
    func type(text: String) {
        self.tap()
        self.typeText(text)
    }
}

テストコード本体

それでは、テストファイルを記述していきます。 自前で用意した拡張関数を利用して、画面操作後の待機を行いテストを進行させます。システム通知への操作は、「springboard」を利用してUIテストを継続していきます。 [hogehogeUITests.swift]

//
//  hogehogeUITests.swift
//

import XCTest
import Foundation

class hogehogeUITests: XCTestCase {
    private let app = XCUIApplication()
    
    override func setUp() {
        // Put setup code here. This method is called before the invocation of each test method in the class.
        super.setUp()
        
        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false
        
        // Delete the old application.
        Springboard().deleteApp()
        
        // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
        app.launch()
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
        app.terminate()
    }
    
    func test_foo() {
        // Use recording to get started writing UI tests.
        // Use XCTAssert and related functions to verify your tests produce the correct results.

        // Click after finding elements which contains texts "ほげほげ".
        app.buttons["ほげほげ"].tap()

        // Enter texts"ふが" in an element which contains texts "ぴよ".

        // Allow notifications form system.
        XCUIApplication(bundleIdentifier: "com.apple.springboard").buttons["Allow"].tapIfExists()
    }
}

筆休め

Swiftでもリグレッションテスト向けの自動化されたITテスト用意しておくことで、コストが低く効果が高いテストを行えます。 アプリの品質が、事業の成否となる場面もあるので、しっかりとしたテストを行っておきたいですね。

以上、「XCUITestでUIテストを自動化する」でした。


※無断転載禁止 Copyright (C) kikkisnrdec All Rights Reserved.