2016/01/06
UIテストに対応したfastlaneのsnapshotでスクリーンショット撮影を自動化
目次
こんにちは。
ご無沙汰しております、R小川です。
最近、ずっと気になっていた「大人のお子様ランチ」を食べました。
私が行ったお店では、オムライス、ハンバーグやカニクリームコロッケなどといった子どもが好きそうなメニューでした。
調べてみたら、意外と色んなお店にあるみたいですよ「大人のお子様ランチ」。
みなさんも童心に帰った気持ちで食べてみてはいかがでしょうか。
さて、今回はfastlaneのsnapshotについてご紹介します。
fastlane
iOS/Androidアプリのリリース作業を自動化するツール群です。
snapshot
fastlaneのツールの1つで、スクリーンショット撮影を自動化するツールです。
これまで、snapshotではUIAutomationによるJavaScriptを使用していましたが、2015年10月にXcode7からのUI Testingに対応した内容へ変わりました。
これによりUI操作の記述は、これまでのJavaScriptからSwiftとObjective-Cに変わりました。
今回はObjective-Cベースで進めていきます。
UIテストの設定については、fastlaneの開発者Felix Krauseさんのブログの記事がとても分かりやすかったです(今回ご紹介する内容は、一部この記事と重なる箇所があります)。
UIテストターゲット作成
Xcode7でプロジェクトを開き、UIテストターゲットを作成します。
File ▶︎ New ▶︎ Targetを選択し、
「iOS UI Testing Bundle」を選択します。
LanguageはObjective-Cを選択します。
プロジェクトのUI Testsフォルダに.mファイル(以下、UIテスト実装ファイル)とInfo.plistが生成されます。
snapshotをインストール
$ sudo gem install snapshot |
最新のXcode command line toolsをインストールする。
$ xcode-select –install |
プロジェクトフォルダ直下で以下のコマンドを実行します。
$ snapshot init |
プロジェクトフォルダ直下に2ファイルが追加されます。
- Snapfile
- SnapshotHelper.swift
SnapshotHelper.swiftをUI Testフォルダに追加します。
以下のようなメッセージが表示されますので、Objective-CとSwiftを共存可能するために「Create Bridging Header」を選択します。
UI Testing
UIテスト実装ファイルを開き、testExampleメソッドにカーソルを当て、左下の赤いレコードボタンを押してシュミレーター上のUI操作をレコーディングします。
UI Automationとの違い
これまで、UIAutomationでIdentifierが設定されていないボタンをタップする操作では、例えば以下のように、画面の上から5番目のボタンをタップするといったスクリプトを自動的に生成してくれていました。
target.frontMostApp().mainWindow().buttons()[5].tap(); |
しかし、UI Testingではそういったことはされず、UI操作のレコーディング中にIdentifierが設定されていないボタンをタップするとクラッシュする事象が発生しました(Xcode7.1)。
したがって、Identifierが設定されていない場合は、以下のようにオブジェクトを特定できるように一意の文字列を設定するか、Interface BuilderのAccessibilityグループのIdentifierで文字列を設定しておくと良いです。
button.setAccessibilityIdentifier = @”スモールライト”; |
必要なUI操作が終了したら、再度レコードボタンを押して、レコーディングを終了します。
UIテスト用スキーム作成
UI TestsターゲットでUIテスト用のスキームを作成します。
Objective-Cのプロジェクトから、先程追加したSnapshotHelper.swiftを呼び出せるようにするため、作成したUIテスト用スキームで一度ビルドします。
これにより、<# UIテストターゲット名 #>-Swift.hがプロジェクトに一つ自動的に生成されます。
snapshot用にUIテスト実装ファイルを修正
UIテスト実装ファイルに以下のようにインポートを追加します。
#import “<# UIテストターゲット名 #>-Swift.h” |
setUp()メソッドに以下を追加します。
XCUIApplication *app = [[XCUIApplication alloc] init]; [Snapshot setLanguage:app]; [app launch]; |
スクリーンショットを撮りたい操作タイミングで以下を追加します。
[Snapshot snapshot:@”01LoginScreen” waitForLoadingIndicator:YES]; |
“01LoginScreen”は、以下のようなデバイスと組み合わせた画像ファイル名になります。
“iPhone6s-01LoginScreen.png” |
複数回撮影する場合は、スクリーンショット毎にこの名称を変更します。
ボタンをタップし画面遷移した直後にスクリーンショットを撮ると読み込み途中の状態でスクリーンショットを撮ってしまうことがあります。
そのため、スクリーンショットを撮る前にsleep(秒数)などを追加して間隔を空けるようにしておくと良いです。
実際にConfitアプリで申請時にスクリーンショット撮ることが多そうな、アプリのトップ画面、タイムテーブル画面、講演検索画面、開催概要画面のスクリーンショットを撮るテストコードを下記のように作成しました。
XCUIApplication *app = [[XCUIApplication alloc] init]; sleep(5); [Snapshot snapshot:@”00TestScreen” waitForLoadingIndicator:YES]; XCUIElement *backPngImage = app.images[@”back.png”]; [backPngImage.buttons[@”dashboard_menu_timetable”] tap]; sleep(5); [Snapshot snapshot:@”01TestScreen” waitForLoadingIndicator:YES]; [[[[app.navigationBars[@”StretchTimeTableVC”] childrenMatchingType:XCUIElementTypeButton] matchingIdentifier:@”戻る”] elementBoundByIndex:0] tap]; [backPngImage.buttons[@”dashboard_menu_programsearch”] tap]; [app.tables.staticTexts[@”条件を指定してさがす”] tap]; sleep(5); [Snapshot snapshot:@”02TestScreen” waitForLoadingIndicator:YES]; [[[[app.navigationBars[@”講演検索”] childrenMatchingType:XCUIElementTypeButton] matchingIdentifier:@”戻る”] elementBoundByIndex:0] tap]; [[[[app.navigationBars[@”SearchProgramVC”] childrenMatchingType:XCUIElementTypeButton] matchingIdentifier:@”戻る”] elementBoundByIndex:0] tap]; [backPngImage.buttons[@”dashboard_menu_outline”] tap]; sleep(5); [Snapshot snapshot:@”03TestScreen” waitForLoadingIndicator:YES]; [[[[app.navigationBars[@”開催概要”] childrenMatchingType:XCUIElementTypeButton] matchingIdentifier:@”戻る”] elementBoundByIndex:0] tap]; |
スクリーンショット撮影前には、先程ご紹介したようにsleep(5);で5秒間隔を空けています。
Snapfile(設定ファイル)修正
# A list of devices you want to take the screenshots from devices([ “iPhone 6s”, “iPhone 6 Plus”, “iPhone 5” ]) languages([ “ja” ]) # The name of the scheme which contains the UI Tests scheme “ConfitAppUITests” # Where should the resulting screenshots be stored? output_directory “./screenshots” # clear_previous_screenshots # remove the ‘#’ to clear all previously generated screenshots before creating new ones clear_previous_screenshots “true” # Choose which project/workspace to use workspace “./ConfitApp.xcworkspace” |
Snapfileで実行時の設定を記述します。
・devices :スクリーンショットを撮りたいデバイス
・languages:端末言語(今回は日本語のみのため「ja」を設定)
・scheme:スキーム(追加したUIテスト用スキームを設定)
・output_directory:生成したスクリーンショットを配置するディレクトリ
・clear_previous_screenshots:前回作成したスクリーンショットを全て削除
・workspace:対象のワークスペースを設定
snapshotでは、他にもいくつかオプション設定があります。
オプションの詳細は、以下のコマンドを実行して確認できます。
$ snapshot -h |
snapshotを実行
それではスクリーンショットを作成します。
snapshotコマンドを実行します。
$ snapshot |
正常に処理が終了すると、スクリーンショットと、それらが一覧で表示されるscreenshots.htmlが生成されます。
先程のテストコードでは以下のように生成されました。
snapshotを試してみて
メリット
- “snapshot”コマンド一発で申請に必要なスクリーンショットが作成できる
(→ 4.7インチ、5.5インチ端末向けのスクリーンショットが申請時に必須に)
- Objective-Cを利用しているので既存のクラスが使用できる
デメリット
- UIテスト時のクラッシュがまだまだ多い気がする
- 端末×言語の回数だけビルド → シュミレータ起動 → UI操作を繰り返すので、その分だけ実行時間がかかる
端末や言語が多かったり、ビルド時間が長かったりする場合には、5.5インチ用のスクリーンショットだけをsnapshotか手作業で撮影し、それを基にLaunchKitのScreenshot Builderやスクリプトなどと組み合わせて他の画面サイズに対応したスクリーンショットを生成した方が早いかもしれません。
緊急時を除き、これまでスクリーンショット撮影に割いていた時間を別のことに使える、そんなツールだと思います。
最後に
最近では、fastlaneにAndroid向けのsupplyが追加されたり、Circle CIにfastlaneがデフォルトでインストールされているようになりました。snapshotだけでなく、今後もfastlaneのツールに注目しつつ上手に活用していきたいです。
日々是精進。