[SwiftUI] [UnitTest] テストの作り方 ( NavigationView + List 編)

SwiftUI component UITest シリーズ NavigationView + List 編

NavigationView と List を使ったアプリのテスト方法を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Catalina 10.15.7
  • Xcode 12.2
  • iOS 14.2

テスト対象アプリ

テスト対象アプリ
テスト対象アプリ
example code

struct ContentView: View {
    @State private var items = ["item1", "item2", "item3"]
    var body: some View {
        NavigationView {
            List {
                ForEach(items, id:\.self) { item in
                    Text(item)
                }
                .onDelete { _ in 
                }
            }
            .accessibility(identifier: "MyList")
            .toolbar{
                #if os(iOS)
                ToolbarItem(placement: .navigationBarLeading) {
                    EditButton()
                        .accessibility(identifier: "EditButton") // does not work
                }
                #endif
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        items.append("item\(Date().timeIntervalSince1970)")
                    }, label: {
                        Label("Add Item", systemImage: "plus")
                    })
                    .accessibility(identifier: "Add") // does not work
                }
            }
            .navigationTitle("NavTitle")
        }
    }
}

テスト対象の要素と操作

以下をテスト対象の要素および操作とします。

対象操作

  • List 表示
  • 編集ボタンによる操作
  • 削除操作
  • 追加操作

対象要素

  • NavigationLink
  • List
  • List の 行
  • NavigationBar
  • NavigationBar のボタン
  • 編集モードでの削除操作

要素の取得方法

List

Accessibility ID を使って、取得することができます。

app.tables["<ID>"] とすることで、取得可能です。

Listの行

Accessibility ID を使って、取得することは、できないようです。

table.rows とすることで、取得可能です。

個別行は、rows.element(boundBy: <index>) として取得することができます。

NavigationLink

Accessibility ID を使って、取得することができます。

app.buttons["<ID>"] とすることで、取得可能です。

NavigationBar

Accessibility ID を使って、取得することは、できないようです。

Title をつけることで、その Title を使って取得することができます。

app.navigationBars["<title>"] とすることで、取得可能です。

NavigationBar のボタン

Accessibility ID を使って、取得することは、できないようです。

表示順(左側)に取得することはできます。

navBar.buttons.element(boundBy: 0) とすることで、取得可能です。

編集モードでのボタン

Accessibility ID を使って、取得することは、できないようです。

特定の ID を持っているようで、

編集モードでの行の左に表示される ❌ ボタンは、"Delete "

❌ボタン押下後に表示される Delete ボタンは、"Delete" という ID で取得できます。

サンプルコード

以下は、アプリ起動後に、Edit ボタンを押下し、削除操作することで、行数が減ることをテストするコードです。

NavigationView + List をテスト

    func test_launch_withInitialData_ListShouldHaveThree() {
        let app = XCUIApplication()
        app.launch()

        // (1)		
        let myList = app.tables["MyList"]
        // (2)		
        let rows = myList.cells
        XCTAssertEqual(rows.count, 3)
        
        // (3)		
        let myNavBar = app.navigationBars["NavTitle"]
        XCTAssertTrue(myNavBar.exists)

        // (4)
        let editButton = myNavBar.buttons["Edit"]
        XCTAssertTrue(editButton.exists)

        // (5)		
        let addButton = myNavBar.buttons.element(boundBy: 1)
        XCTAssertTrue(addButton.exists)
        //XCTAssertEqual(addButton.label, "Add Item") // does not work

        // delete 2nd line
        // (6)		
        editButton.tap()
        // (7)		
        rows.element(boundBy: 1).buttons["Delete "].tap()
        // (8)		
        rows.element(boundBy: 1).buttons["Delete"].tap()

        XCTAssertEqual(rows.count, 2)
    }
コード解説
  1. MyList という ID を持つ List を取得
  2. List に含まれる行を取得
  3. NavTitle という Title を持つ NavigationBar を取得
  4. EditButton を取得
  5. 追加ボタンを取得
  6. EditButton をタップ
  7. 2行目の左側にある ❌ をタップ
  8. 2行目の右側に表示される Delete をタップ

所感

NavigationView のテストについては、動作が不安定な気がしますので、リリースごとに動作が変わることが予想されます。

まとめ:NavigationView と List を使ったアプリのテスト

NavigationView と List を使ったアプリのテスト
  • tables 経由で、List を取得する
  • navigatioinBars 経由で NavigationBar を取得する
  • tables.rows 経由で各行を取得する
  • SwiftUI から提供されている EditButton やその後の削除操作要素の要素は、特定の ID がふられている

説明は以上です。
不明な点やおかしな点ありましたら、
こちらまで。

コメントを残す

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