[SwiftUI] [UnitTest] テストの作り方 ( Text を例として)

UI テスト の書き方って、あまり説明されていないと思うので、基本的なところから説明していきます。非常にシンプルな要素である Text を例として説明します。
TDD[SwiftUI] [UnitTest] [Xcode] Xcode を使ったテストの始め方

テスト環境&対象

テスト環境&対象
  • macOS Catalina 10.15.7
  • Xcode 12.1
  • SwiftUI で作られたアプリケーション
  • SwiftUI Text コンポーネント

テスト項目

テスト設計は非常に重要です。何をしたら、どうなる。

Text は、非常にシンプルな要素なので、ここでは、「何が表示されているか」をテスト対象とします。

アプリケーションのライフサイクルで特に変化せず、常に同一テキストが表示されている想定で、何が表示されているかをテストするものとします。

今回は、サンプルのプロジェクトを作成すると表示される Hello, world! が表示されているかを確認します。

UnitTest / UITest

Xcode での test には、おおきく2種類あります。 UnitTest と UITest です。

UnitTest は、アプリケーションの機能確認用のテストを書くためのもので、アプリケーションを関数レベルでテストすることに使用されます。

UITest は、アプリケーションの動作を UI (User interface) を経由して確認するテストで、実際のユーザーが行うような操作を行い、期待通り動作するかを確認するテストに使用されます。

Xcode の プロジェクト作成時に "include Tests" にチェックを入れると、Unit Test と UI Test の2つが作成されます。

これらのテストは、プロジェクトに追加された2つのターゲット <プロジェクト名>Tests と <プロジェクト名>UITests として確認することができます。

今回は、Text コンポーネントの動作を、アプリケーションの外側から確認するので、UITest の一部として実行されるテストを作成することになります。

UITest テンプレート

UITest 向けには、以下のようなコードがテンプレートとして生成されます。

UITest テンプレート

//
//  TDDTextUITests.swift
//
//  Created by : Tomoaki Yagishita on 2020/10/27
//  © 2020  SmallDeskSoftware
//

import XCTest

class TDDTextUITests: XCTestCase {
  // (1)
  override func setUpWithError() throws {
    // Put setup code here. This method is called before the invocation of each test method in the class.
    
    // In UI tests it is usually best to stop immediately when a failure occurs.
    continueAfterFailure = false
    
    // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
  }
  
  // (2)
  override func tearDownWithError() throws {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
  }
  
  // (3)
  func testExample() throws {
    // UI tests must launch the application that they test.
    let app = XCUIApplication()
    app.launch()
    
    // Use recording to get started writing UI tests.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
  }
  
  // (4)
  func testLaunchPerformance() throws {
    if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
      // This measures how long it takes to launch your application.
      measure(metrics: [XCTApplicationLaunchMetric()]) {
        XCUIApplication().launch()
      }
    }
  }
}

以下のような関数が定義されています。

(1) setupWithError 関数
テスト1つ1つの実行前にこの関数が実行されます。テストの前提条件を整える等に使われます
(2) tearDownWithError 関数
テスト1つ1つの実行後にこの関数が実行されます。テスト実行後の後処理等に使われます
(3) testExample 関数
テストコードのサンプルです。この関数では、アプリケーションを起動するところまでがサンプルとして記述されています
(4) testLaunchPerformance 関数
アプリケーションの起動時間のパフォーマンスをテストするコードのサンプルです。この記事では説明しません。

testLabelValue

testExample をベースとして、ラベルの値をテストする testLabelValue を書いてみます。

testLabelValue code

  func testLabelValue() throws {
    // UI tests must launch the application that they test.
    let app = XCUIApplication()
    app.launch()
    // (1)
    let text = app.staticTexts["TextHelloWorld"]
    // (2)
    XCTAssertTrue(text.exists)

    // (3)
    XCTAssertEqual(text.label, "Hello, world!")
  }
コード解説
  1. アプリケーションから、ID "TextHelloWorld" を持つテキストを検索します
  2. 検索してきたテキストが存在するかをテストします
  3. テキストのラベルが、"Hello, world!" であるかをテストします

上記のテストを成功させるためには、アプリケーションコード側にも変更が必要です。

Accessibility ID

非常に小さく簡単なテストですが、要素をどのように取得してくるかが、このテストのポイントです。

アプリケーション中の要素は、XCUIElementQuery を使用して探すことができます。

Apple のドキュメントは、こちら

XCUIApplication も 親クラスである XCUIElement が、XCUIElementTypeQueryProvider に準拠しているために使用できます。

App.staticTexts で、アプリケーション中の Text を取得することができます。ただし、全ての Text を取得できてしまうので、その中から該当する Text を指定する必要があります。

そこで使用されるのが、accesibility の ID です。この ID を使用して、特定の Text を取得することができます。

SwiftUI では、.accessibility modifier を使って、この ID を指定することができます。

MEMO
UIKit では、Storyboard 上で指定することもできました。

以下は、今回のテスト用に、accessibility modifier を追加したコードです。

accesibility を追加したコード

//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2020/10/27
//  © 2020  SmallDeskSoftware
//

import SwiftUI

struct ContentView: View {
  var body: some View {
    Text("Hello, world!")
      .padding()
      .accessibility(identifier: "TextHelloWorld")
      // (1)
  }
}
コード解説
  1. accessibility modifier を使用して、Text に "TextHelloWorld"という ID を付与

Text のラベル

取得できた Text のラベル値は、label プロパティで取得できます。

Hello, world! であることのテストは、以下のようなコードになります。

Text label テスト

XCTAssertEqual(text.label, "Hello, world!")

まとめ:SwiftUI の Text をテストする

SwiftUI の Text をテストする
  • XCUIApplication から、accessibility ID 指定で Text を取得することができる
  • XCUIApplication からは、staticTexts["ID"] で Text を取得することができる
  • Text のラベルは、label プロパティで取得できる
  • SwiftUI では、accessibility ID は、.accessibility modifier で付与する
  • XCTAssert でテストする

説明は以上です。
不明な点やおかしな点ありましたら、ご連絡いただけるとありがたいです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です