見出し画像

GitHub ActionsでAppiumの画像セレクタを使う

こんにちは、自動化エンジニアの森川です。
今日はAppiumの画像セレクタ機能を用いたE2EテストをGitHub Actions上で動かしてみたいと思います。

きっかけ

Appiumの画像解析機能は、画像による要素指定や画像マッチングができるのでユースケースによっては非常に有用だと思います。ただしAppium本体とは別にopencv4nodejsが必要なのでセットアップにひと手間がかかります。ただでさえセットアップに手間のかかるAppium環境に、もうひと手間かかるのは正直つらいです。

また、筆者はWindowsユーザーなのでiOSの検証環境が常に手元にありません。普段はクラウドテストサービスで済ませていますが、opencv4nodejsはクラウド側の環境に準備されていなかったり、対応が限定的だったりします※1。そこでGitHub ActionsのHosted-Runnerを使ってクラウド上で実現できないかと思い立ちました。

コードはこちらに公開しています。
tmshft/appium-image-detection-example

尚、本記事ではAppiumやGitHub Actionsの基本的なところは触れません。

※1 BrowserStackではリアルデバイスのみ対応しているようです。SauceLabsは2018年からリクエストされたままです。
Requesting support for OpenCV - Sauce Labs Customer Idea Portal

トライアル環境

・Java:AdoptOpenJdk11
・Gradle:6.8.3
・Appium Java Client:7.4.1
・Appium: 1.18.3
・opencv4nodejs: 3.4.6
・macOS:10.15(latest)
・XCode:12.0.1
・iOS:14.0(Simulator)

参考リンク
Image Comparison - Appium
Finding Image Elements - Appium

Hosted Runner

MacOS10.15Hosted-runnerを使います。今回は利用制限の無いPublicリポジトリで挑戦しています。理由はビルドに時間がかかり、Privateリポジトリだと利用制限にすぐ達してしまうからです。特に時間を要するのはopencv4nodejsの導入で1stepに15分以上かかるなんていうのもザラです。このあたりはEnterpriseプランであればビルド資産をCacheする方がよさそうです。

※ 本記事ではGitHub Actionsをクラウドデバイスファームのような使い方をしています。本来このような用途で使用するのはActionsの設計思想から離れているという見方もあると思います。Actionsに準備されたHosted Runnerでネイティブなセットアップがどこまでシンプルにできるか一例を示すトピックと考えて読んでいただけると幸いです。

テストケース

検証アプリは弊社のテストツール検証用アプリを使いました。

画像1

イメージで要素を選択してクリック、テンプレート・マッチングで結果確認するというシンプルなテストです。

Appium設定

DesiredCapabilities readCapabilities() {
  DesiredCapabilities capabilities = new DesiredCapabilities();
  //...(中略)
  capabilities.setCapability("settings[getMatchedImageResult]", true);
  capabilities.setCapability("settings[imageMatchThreshold]", 0.8);
  return  capabilities;}

・getMatchedImageResult:イメージセレクタの結果画像データ維持
・imageMatchThreshold:比較時のしきい値設定

テストコード

@Test
void imageSelectorTest() throws IOException, InterruptedException {
   // イメージセレクタ
   WebElement loginElm = driver.findElementByImage(getReferenceImageB64("racine_sut_login.png"));
   // visualization
   String imageResult = loginElm.getAttribute("visual");
   // Allureにアタッチ(サービス関数経由)
   addAttachment(Base64.getDecoder().decode(imageResult), "image-selector");
   loginElm.click();

   Thread.sleep(10000);
   // テンプレート・マッチング
   OccurrenceMatchingResult result = templateMatch("original_template.png");
   // Allureにアタッチ(サービス関数経由)
   addAttachment(Base64.getDecoder().decode(result.getVisualization()), "template-matched-result");
   
   assertNotNull(result.getRect());
}

イメージセレクタは、AppiumDriverのfindElementByImage APIにイメージのデータをbase64形式で渡すだけです。
テンプレート・マッチングはfindImageOccurrence APIを用います。こちらは2つのイメージファイルとオプションを渡しています。

// サービス関数
private OccurrenceMatchingResult templateMatch(String template) throws IOException {
   return driver
           .findImageOccurrence(
                   driver.getScreenshotAs(OutputType.FILE),
                   new File(resourceDir + template),
                   new OccurrenceMatchingOptions()
                           .withThreshold(0.6)
                           .withEnabledVisualization());
}

ワークフロー

PRされたらワークフローが走ってテストを実行して結果をPRにコメントするという流れです。

name: Appium iOS gui test example
on:
 pull_request:
   branches:
     - "*"
jobs:
 build:
   name: Native App Test
   runs-on: macos-latest
   steps:
     - uses: actions/checkout@v1
       name: checkout

     - name: checkout app as git submodule
       run: |
         echo "${{ secrets.codecommit_key }}" > ~/.ssh/id_rsa
         chmod 600 ~/.ssh/id_rsa
         export GIT_SSH_COMMAND="ssh -v -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -l ${{ secrets.codecommit_id }}"
         git submodule add --force ${{ secrets.codecommit_sut_repo }} ./sut_app
         git submodule update -i --remote

     - name: specify valid Xcode Version
       run: sudo xcode-select -s /Applications/Xcode_12.0.1.app

     - name: set up node.js
       uses: actions/setup-node@v1
       with:
         node-version: '10.16.3'

     - name: set up opencv4nodejs
       run: |
         brew install cmake
         npm install -g opencv4nodejs

     - name: set up Appium
       run: |
         npm install -g appium@1.18.3
         appium --log-timestamp --log-no-colors > appium.log &

     - name: execute test
       run: |
         chmod +x ./gradlew
         ./gradlew clean test allureReport

     - name: deploy to pages
       uses: peaceiris/actions-gh-pages@v3
       with:
         github_token: ${{ secrets.GITHUB_TOKEN }}
         publish_dir: build/reports/allure-report
         publish_branch: test-report
       if: always()

     - name: comment to pr
       uses: actions/github-script@0.3.0
       if: github.event_name == 'pull_request'
       with:
         github-token: ${{ secrets.GITHUB_TOKEN }}
         script: |
           const { issue: { number: issue_number }, repo: { owner, repo }  } = context;
           github.issues.createComment({ issue_number, owner, repo, body: 'ci-test report<br/><a href="[repository url]">see report</a>' });

CIパイプライン全般に言えるのですが、デバッグができないのでステップごとに慎重に書いていくしかないです。ときにはls,pwdコマンドを使ってヨチヨチと確認をして問題解決していきます。
※ SSHでデバッグできるコマンドがあると社内指摘をいただきました。今回は試していません、次回に是非!

各ステップはコメントのとおりなので、ポイントだけ抜粋します。

- name: checkout app as git submodule

検証用アプリをGitのSubModuleとして某所からチェックアウトしています。アカウント情報はあらかじめGitHubのSecretsに設定しておきます。

- name: set up opencv4nodejs

opencv4nodejsのインストール

- name: execute test

テスト実行とあわせてレポートの出力のGradleタスクallureReportを実行します。

deploy to pages ~ comment to pr

結果レポートをGitHub Pagesにパブリッシングして、PRスレッドにコメント投稿しています。
いずれもGitHubマーケットプレースでよさそうだったActions(Module的なもの)を使わせてもらいました。
※ GitHub Marketplaceにはセキュア上よろしくないActionsも公開されているそうなのでご利用には十分ご注意ください。

実行結果

実行結果を見てみましょう。
現物はこちらです。
https://tmshft.github.io/appium-image-detection-example/

結果レポートは美しさで定評のAllure Reportを使っています。ActionsのArtifactにアップロードするだけでもよいのですが、ZIPファイルをダウンロードして閲覧する手間がとっても面倒なので、GitHub Pagesで1クリックで見られるようにしています。

画像2

イメージセレクタ
要素検索に使用したイメージファイル

画像3

一致結果

画像5

イメージセレクタとして認識された要素が赤線で囲まれているのがわかります。イメージセレクタのマッチ画像取得はたった1行書くだけです。

// Visualization
String imageResult = loginElm.getAttribute("visual");
// Allureレポートにアタッチ(サービス関数経由)
addAttachment(Base64.getDecoder().decode(imageResult), "image-selector");

ただし上述のとおりCapabilitiesでgetMatchedImageResultをtrueにしておく必要があります。

テンプレートマッチ
テンプレート・イメージファイル

画像5

ログイン後の画面に表示される「テスト項目」のイメージが含まれているかどうかを検証しています。

画像6

赤線で囲まれていますね。(サンプルアプリの地色も弊社のイメージカラー「赤」なので恐ろしくわかりにくいですね!)

マッチ結果はgetVisualizationで取得します。
こちらも事前にOccurrenceMatchingOptions.withEnabledVisualizationしておきます。

まとめ

Appiumとopencv4nodejsの面倒なセットアップからテストとレポーティングまでを、実にわずかなコードで書けることがわかりました。物理端末のセットアップとくらべると作業時間の差は歴然だと思います。ただクラウド+リモートなので、トラブルシューティングが難しいという弱点はあります。
そして本トライアルはあくまでサンプルです。実際の運用では様々な要素が絡み合いますので、解決に追われることになります。

DPI値
デバイスによってDPI値が異なりますので比較イメージをリサイズしてやる必要があります。OSによって表示内容が変わる場合はそれぞれの要素画像を用意しないといけません。

要素が見つからない場合の対策
要素が見つからない場合の分岐処理や画像比較時のしきい値のチューニングが必要になるでしょう。

パフォーマンス
画像判定処理をしている分、当然パフォーマンスは下がります。もとより速いとは言えないAppiumなので、テスト実行の全体にかかる時間が膨らみすぎないように工夫が必要になるでしょう。

キャッシュ化
これはGitHub Actions側の問題ですね。アプリケーションを毎回クリーンインストールしていると時間がかかりますのでキャッシュで高速化するべきですが、無限ではないので今度はキャッシュ管理という手間が発生します。

また今回のトライアルの状態ではレポートが実行毎に上書きされてしまいますので、履歴を残すようにするべきです。
このように、さっと思いつくだけでも様々な点について考慮する必要があります。

おまけ

最後に宣伝です。
弊社のGUI自動化ツールRacineではPC-Webブラウザでもイメージセレクタ機能をご利用いただけます。またGUI自動テストでのGitHub Actions導入実績やノウハウがございます。
SHIFTのGUIテストツールRacineとその開発プロセス

お客様のニーズにあわせて自動化アーキテクトが過去のナレッジを生かした柔軟な対応をすることで最適なE2E自動テストを提供いたします。

__________________________________

執筆者プロフィール:森川 知雄
中堅SIerでテスト管理と業務ツール、テスト自動化ツール開発を12年経験。
SHIFTでは、GUIテストの自動化ツールRacine(ラシーヌ)の開発を担当。
GUIテストに限らず、なんでも自動化することを好むが、ルンバが掃除しているところを眺めるのは好まないタイプ。
さまざま案件で自動化、効率化による顧客への価値創出を日々模索している。

画像7


あなたの「スキ」が励みになります!
「無駄をなくしたスマートな社会の実現」を目指し、ソフトウェア製品の開発、運用、マーケティングなど、あらゆる立場から携わるSHIFT Groupの公式noteです。エンタメ業界から、Web系、金融/製造/小売りなどのエンタープライズ業界まで広い知見を活かした情報を発信しています。