sample-xcuitest

Xcodebuild-UITest Xcodebuild-UITest-Another-Report Fastlane-UITest Fastlane-UITest-Another-Report Fastlane-UITest-Parallel

SwiftでのUI系自動テスト(XCUITest, XCUtest)のサンプルです。

プロジェクト

Target名 説明 利用ライブラリ
UITestSample アプリのターゲット。UIKit利用。
UITestSampleTests UnitTestのターゲット。
「スナップショットテスト (その1)」のコードを格納。
ios-snapshot-test-case
・XCTest(標準)
UITestSampleTests2 UnitTestのターゲット。
「スナップショットテスト (その2)」のコードを格納。
swift-snapshot-testing
・XCTest(標準)
UITestSampleUITest UIテストのターゲット。Page Object Patternを導入。 ・XCTest(標準)
・XCUITest(標準)
Scheme名 Test Target
SnapShot UITestSampleUITests
SnapShotTest UITestSampleTests
SnapShotTest2 UITestSampleTests2
UITest UITestSampleUITests

ローカル実行

fastlane

ruby, bundlerは導入済みの前提。

bundle install --path=.bundle
bundle exec fastlane ui_test

各laneの詳細。

lane 起動するScheme 内容 呼出サンプル
ui_test UITest UIテスト実施 ./run/fastlane-uitest.sh
make_report - UIテストのレポート生成
build_for_ui_test UITest UIテスト用のビルド(分散実行用) ./run/fastlane-uitest-parallel.sh
ui_test_without_building UITest UIテスト実施(分散実行用、テストクラス指定あり) ./run/fastlane-uitest-parallel.sh
snapshot SnapShot スナップショット取得(Fastlaneの標準action snapshotを使用) ./run/fastlane-snapshot.sh
capture_snapshot SnapShotTest スナップショットテスト(その1)用の正解画像を取得 ./run/fastlane-snapshot-test1-1-capture.sh
snapshot_test SnapShotTest スナップショットテスト(その1)を実施 ./run/fastlane-snapshot-test1-2-testrun.sh
snapshot_test2 SnapShotTest2 スナップショットテスト(その2)を実施(初回は正解画像を取得) ./run/fastlane-snapshot-test2-testrun.sh

CI

GitHub Actions

下記5つのworkflowを定期実行中。

workflow yml 処理内容 レポート形式 定期実行? 手動実行可能?
Xcodebuild-UITest xcodebuild-uitest.yml UIテスト実施(xcodebuildで起動) XCTestHTMLReport
Xcodebuild-UITest-Another-Report xcodebuild-uitest-another-report.yml UIテスト実施(xcodebuildで起動) xcresulttool
Fastlane-UITest fastlane-uitest.yml UIテスト実施(fastlaneで起動) XCTestHTMLReport
Fastlane-UITest-Another-Report fastlane-uitest-another-report.yml UIテスト実施(fastlaneで起動) xcresulttool
Fastlane-UITest-Parallel fastlane-uitest-parallel.yml UIテスト並列実施(fastlaneで起動) -

詳細

詳細を見る

スナップショット撮影

下記で./snapshots以下にスナップショットが保存される。 提出用画面の説明等にも使える。 fastlaneの標準actionであるsnapshotの機能で実現している。

bundle exec fastlane snapshot run --scheme "SnapShot" --configuration "Release"  --sdk "iphonesimulator"

スナップショットテスト (その1)

ios-snapshot-test-caseを使った方法。

正解画像取得

下記で./snapshot_tests/ReferenceImages_64に正解画像が保存される。

bundle exec fastlane capture_snapshot tests:UITestSampleTests/MainViewControllerTests/testMainViewSnapshot

テスト実施

下記で正解画像との比較が実施される。

bundle exec fastlane snapshot_test tests:UITestSampleTests/MainViewControllerTests/testMainViewSnapshot 

スナップショットテスト (その2)

swift-snapshot-testingを使った方法。

参考:メルペイiOSチームのスナップショットテストを効率化した話 | メルカリエンジニアリング

正解画像取得、テスト実施

下記で./__Snapshots__/*に正解画像が保存される(初回実行時)。 その後同じコマンドで正解画像との比較が実施される。

bundle exec fastlane snapshot_test2 tests:UITestSampleTests2/MainViewControllerTests/testMainViewSnapshot 

並列実行

前提

  • ビルドとテスト実施を分ける

    • build-for-testingtest-without-buildingで分ける (xcodebuildのコマンド)
  • テストメソッドは小分けにする

    • XCTestCase単位で分散される模様
    • 同じXCTestCaseに実装されたテストは分散されない
    • 下記のようにテスト実施クラスを小分けにする必要がある
    class AzimoUITestsLogin : XCTestCase {
      func test_login() {code}
    }
    class AzimoUITestsCreateRecipient : XCTestCase {
      func test_createRecipient()
    }
    class AzimoUITestsCreateTransfer : XCTestCase {
      func test_createTransfer() {code}
    }
  • シミュレーター関連の動作

    • シミュレーターを起動しているとGUIで動作確認可能(並列指定の場合、cloneが複数台立ち上がる)
    • シミュレーターが1台も起動していない(アプリプロセス自体が無い)と、裏で動く

並列実行パターンと結果まとめ

Mac(local)で下記パターンの並列実行を試した。

パターン 結果(テストケースを分散) 結果(機種で分散)
Xcodeから 試してない
xcodebuildコマンド × 試してない
xcodebuildコマンド (xctestproducts指定) × 試してない
fastleneで並列実行 ×

各パターン詳細

Xcode(GUI)で並列実行

シミュレーターが複数立ち上がり、各OS上でテストが並列実行される。

xcodebuildで並列実行

結果

試したコマンド

# build
xcodebuild clean build-for-testing \
  -project UITestSample.xcodeproj \
  -scheme UITest \
  -sdk iphonesimulator \
  -derivedDataPath ./build

# ui test
xcodebuild test-without-building \
  -destination 'OS=16.2,name=iPhone 14' \
  -parallel-testing-enabled YES \
  -parallel-testing-worker-count 4 \
  -maximum-concurrent-test-simulator-destinations 4 \
  -maximum-parallel-testing-workers 4 \
  -xctestrun ./build/Build/Products/UITestSample_iphonesimulator16.2-arm64-x86_64.xctestrun
  • 注意(メモ)
    • -sdk iphonesimulatorを忘れるとtestで失敗する
    • buildが初回こけたので-allowProvisioningUpdatesをつけて1度だけ実行した
xcodebuildで並列実行 (xctestproducts指定)
fastlaneで並列実行

結論

  • fastlaneコマンド単体レベルでは、テストケースの分散・並列実行は出来ていない
    • GitHub Actionsで複数runnerを立ち上げて、テストケース指定で並列分散実行することは可能 (ただ遅い..)
  • 複数機種で同じテストケースを並列実行することは可能

fastlane run_tests (scan) の並列実行関係のパラメータ

parallel_testing: true,
concurrent_workers: 4,
max_concurrent_simulators: 4,
disable_concurrent_testing: false,

1機種指定

devices: ["iPhone 14"]
  • 1台のシミュレーターでテスト1〜4が実行される
  • concurrent_workers: 4にしても勝手にテストケースを分散実行してくれるわけではない (下記動作になる)
    • シミュレーターは4台起動する
    • が、動作しているのは1台でその1台でテストがシリアルに実行される
    • 実行時間的に見て、裏で動く場合(シミュレータ起動無し)もシリアル動作と思われる
    • 事前にシミュレータを起動させても別のcloneが立ち上がり同様のシリアル動作となる

複数機種指定

devices: ["iPhone 14", "iPhone 14 Pro", "iPhone 14 Plus", "iPhone 14 Pro Max"]
  • 各シミュレーター(4台)で同じテスト(テスト1〜4)が並列に実行される

部分実行

-only-testing:を使う。拡張子(.swift)まで指定すると動かなかった。

xcodebuild test-without-building \
    -scheme UITestSample \
    -destination 'OS=16.2,name=iPhone 14' \
    -only-testing:UITestSampleUITests/SampleUITests/testメインページのテスト

参考