【Swift/Xcode】UI Test(User Interfaceテスト)の作成方法

この記事からわかること

  • SwiftXCTestフレームワークとは?
  • XcodeUI Test(User Interfaceテスト)を作成する方法
  • 「UI Test(User Interface)テスト」と「Unit Test(単体テスト)」の違い
  • テストコード生成方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

iOSアプリ開発においてXCTestフレームワークを使用したUIテストの作成方法をまとめていきます。

UI Test(User Interfaceテスト)とは?

UI Test(User Interfaceテスト)はiOSアプリのユーザーインターフェース(UI)の機能と外観を検証するためのテスト方法です。実際のユーザーが行う操作をシミュレートしながらアプリケーションの正常な動作を確認することが目的になります。

全てのインタフェースをチェックしていては埒が開かないので、主要な機能に関わる部分を重点的にチェックしていきます。機能的なところ以外にもレスポンシブ性やレイアウト、コンテンツの表示速度などもテスト対象になります。

例えば以下のようなテストを行います。

XCTestでのUI Test

公式リファレンス:XCTestフレームワーク

XcodeではXCTestフレームワークを使用して機能的な部分のUI Testを簡単に実装できるようになっています。

プロジェクトの作成時に「Include Tests」にチェックを入れていれば最初から組み込まれます。

Xcodeの新規プロジェクト作成画面

プロジェクト作成後からテストを導入したい場合はXcodeの上部メニューから「File」 > 「New」 > 「Target...」をクリックし、Test内の「UI Testing Bundle」を選択して「NEXT」をクリックします。

【Swift/Xcode】XCTestの使い方!Unit Test(単体テスト)を作ろう

ナビゲータエリアにテストターゲットが表示されていれば導入は成功です。(画像はUnit Testも一緒に組み込まれています)。

【Swift/Xcode】XCTestの使い方!Unit Test(単体テスト)を作ろう

おすすめ記事:【Swift/Xcode】XCTestの使い方!Unit Test(単体テスト)の作成方法

UI Test(User Interfaceテスト)の作成方法

今回はUI Test(User Interfaceテスト)を実際に作成してみます。テストターゲットを追加すると「[プロジェクト名]UITests.swift」というファイルが作成され、中には以下のようなコードが記述されています。


import XCTest

final class TestUITests: XCTestCase {

    override func setUpWithError() throws {
        continueAfterFailure = false
    }

    override func tearDownWithError() throws {
    }

    func testExample() throws {
        let app = XCUIApplication()
        app.launch()
    }

    func testLaunchPerformance() throws {
        if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
            measure(metrics: [XCTApplicationLaunchMetric()]) {
                XCUIApplication().launch()
            }
        }
    }
}

コードの中身はUnit Testとほぼ同じですがsetUpWithErrorメソッドとtestExampleの中身が少し異なり、以下の2行が追加されています。

continueAfterFailure = false
let app = XCUIApplication()
app.launch()

continueAfterFailure = falseはテスト実行後に失敗(Failed)した際に中断するかどうを決めるフラグです。UIテストの場合は期待通りに動かなかった時にすぐにアプリを停止させるようにします。falseを渡すことで中断してくれます。

XCUIApplication().launch()アプリを起動させる処理です。

実装の手順

テスト対象コードの準備

今回は単純なUIテストとして「User登録処理」が正しく動作するか確認してみます。期待する動作の流れは以下の通りです。

  1. TextFieldに名前を入力
  2. 「+」ボタンをタップ
  3. 入力した名前がリストに追加される
【Swift/Xcode】UI Test(User Interfaceテスト)の作成方法

struct ContentView: View {
    
    @State  var name:String = ""
    
    @State  var users: [User] = [
        User(name: "Johnny"),
        User(name: "Michael")
    ]
    
    var body: some View {
        NavigationView {
            VStack{
                
                TextField("Name", text: $name).textFieldStyle(.roundedBorder)
                
                List(users, id: \.id) { user in
                    Text(user.name)
                        .font(.headline)
                }
            }
            .navigationBarTitle("User List")
            .navigationBarItems(trailing:
                                    Button(action: {
                self.users.append(User(name: name))
            }) {
                Image(systemName: "plus")
            }.accessibility(identifier: "addButton")
            )
        }.navigationViewStyle(StackNavigationViewStyle())
    }
}

struct User: Identifiable {
    let id = UUID()
    var name: String = ""
}

UI操作の記録

どのようにUI Testを実行するかというとシミュレーターの操作を記録してくれる機能が用意されているのでそれを使用します。

[プロジェクト名]UITests.swift」内のテスト用メソッドにカーソルを合わせ左下の赤丸をクリックするとシミュレーターが起動して記録を開始します。記録されるのはビューのタップやスクロール、入力などで、UI操作を行うたびにどんどんコードが自動で生成されていきます。

【Swift/Xcode】UI Test(User Interfaceテスト)の作成方法

テストコードの作成

自動生成されたコードはそれほど精度が高くないので、不要な操作や期待と違う処理を修正して適切な流れになるように調整します。

func testExample() throws {
    let app = XCUIApplication()
    app.launch()

    // 以下自動生成されたコードを元に編集
    app.textFields["Name"].tap()
    app.textFields["Name"].typeText("Bob")
    app.navigationBars["User List"].buttons["addButton"].tap()
    XCTAssertTrue(app.collectionViews.staticTexts["Bob"].exists)
}

上記のコードは以下のような動作を表しています。

func testExample() throws {
    // アプリ操作用インスタンス生成
    // アプリ起動

    // 以下自動生成されたコードを元に編集
    // Name:TextFieldをタップ
    // Name:TextFieldにテキスト「Bob」を入力
    // User List:NavigationBar > addButton:Buttonをタップ
    // CollectionView内のセルにBobが存在するかチェック
}

テストの実行

これで準備が整ったのでテストを実行してみます。テストごとに「開始ボタン」が左側に表示されるのでクリック、もしくはCommand + Uで実行できます。

【Swift/Xcode】XCTestの使い方!Unit Test(単体テスト)を作ろう

テストが期待通りに実行できると以下のように表示されます。

【Swift/Xcode】XCTestの使い方!Unit Test(単体テスト)を作ろう

UI Test構築に使えるメソッド

自動で生成されたコードからある程度メソッドの使い道が見えてきます。よく使う操作メソッドをまとめておきます。

ビューを取得する

launchメソッドでアプリを起動させたら、XCUIApplicationインスタンスから画面に表示されているButtonTextFieldなどの全てのビューにアクセスできます。

画面に複数あるビューを識別するために識別子を指定します。識別子はButtonなどのlabelに設定している文字列です。

let text = app.staticTexts["Text"]
let textField = app.textFields["Name"]
let btn = app.buttons["ログイン"]

タップする

画面をタップするにはtapメソッドを使用します。

btn.tap()

TextFieldに文字を入力する

TextField文字を入力したい場合は、typeTextメソッドを使用します。引数に入力したい文字列を渡します。またクリアーしたい場合はclearTextメソッドを使用します。

textField.typeText("Hello World")
textField.clearText()

ビューが存在するかどうか

例えば画面遷移をした際などに次の画面に正しく遷移したかを確かめるためにビューが存在するかどうかを取得することがあります。検証するためのXCTAssertTrueなどと一緒に使われることが多いです。

XCTAssertTrue(btn.exists)

ビューの識別子を指定する

labelに設定した文字列でビューを識別するのには同名のラベルの場合など、限界があります。その際はビュー側にaccessibilityidentifbleメソッドを使用して明示的に識別子を指定します。

Text("テキスト")
.accessibilityidentifble("MyText")

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index