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」を選択します。

スクリーンショット_2015-12-01_10_23_37

LanguageはObjective-Cを選択します。

スクリーンショット_2015-12-04_14_32_07

プロジェクトの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」を選択します。

スクリーンショット 2015-12-01 11.12.21

UI Testing

UIテスト実装ファイルを開き、testExampleメソッドにカーソルを当て、左下の赤いレコードボタンを押してシュミレーター上のUI操作をレコーディングします。

スクリーンショット 2015-12-04 16.08.28

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操作が終了したら、再度レコードボタンを押して、レコーディングを終了します。スクリーンショット 2015-12-01 11.06.50

UIテスト用スキーム作成

UI TestsターゲットでUIテスト用のスキームを作成します。

スクリーンショット 2015-12-04 16.53.27

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が生成されます。
先程のテストコードでは以下のように生成されました。

スクリーンショット 2015-12-16 17.59.12

snapshotを試してみて

メリット

  • “snapshot”コマンド一発で申請に必要なスクリーンショットが作成できる

  (→ 4.7インチ、5.5インチ端末向けのスクリーンショットが申請時に必須に)

  • Objective-Cを利用しているので既存のクラスが使用できる

デメリット

  • UIテスト時のクラッシュがまだまだ多い気がする
  • 端末×言語の回数だけビルド → シュミレータ起動 → UI操作を繰り返すので、その分だけ実行時間がかかる

端末や言語が多かったり、ビルド時間が長かったりする場合には、5.5インチ用のスクリーンショットだけをsnapshotか手作業で撮影し、それを基にLaunchKitのScreenshot Builderスクリプトなどと組み合わせて他の画面サイズに対応したスクリーンショットを生成した方が早いかもしれません。

緊急時を除き、これまでスクリーンショット撮影に割いていた時間を別のことに使える、そんなツールだと思います。

最後に

最近では、fastlaneにAndroid向けのsupplyが追加されたり、Circle CIにfastlaneがデフォルトでインストールされているようになりました。snapshotだけでなく、今後もfastlaneのツールに注目しつつ上手に活用していきたいです。

日々是精進。