fastlaneとAppiumでアプリのE2Eテストを自動化する


こんにちは、freeeのモバイルエンジニア @yonekawa です。
この記事は freee Engineers Advent Calendar 2015 3日目です。

背景

サーバーサイドのAPIと通信するモバイルアプリにおいては、サーバー側の変更によってアプリの動作が壊れていないことを保証するのは重要なポイントです。
しかし、freeeのサーバーサイドは日々ものすごい勢いで更新されており、これを人力で確認するのはとても大変なため、自動化する必要があります。

freeeではモバイルアプリのテストを自動化するため、Appiumを使っています。
AppiumはRSpecで、アプリケーションのUIを実際に操作するテストを記述できるので、Railsに親しんでいるfreeeのエンジニアにとって馴染みのあるシンタックスでE2Eのテストが記述できます。
Webの自動テストにはSeleniumを使っているので、その点でもfreeeとは相性のいいツールでした。

しかし、Appium用のバイナリはDeveloper証明書で署名しないといけなかったり、SimulatorでテストするためにSimulator SDKでビルドしないといけなかったり、テストを実行するまでの環境を用意するのは意外と面倒です。
Appiumサーバー自体の不安定さもあってテスト用の環境はQAチームがメンテナンスしており、モバイルアプリのCI連携がなかなかうまくできていませんでした。
Jenkinsで作られたAppium用ビルドをQAに渡し、QAの環境でAppiumを実行するという手間の多いフローで行われていました。

面倒だったのでfastlane用のアクションを作った

さすがに面倒だったので、最近導入したfastlaneからAppiumを使ってテストを自動化できるアクションを作りました。
https://gist.github.com/yonekawa/a66020d08d3fb1ec6457

Fastfileには以下のように定義します。
本当はビルドにgymを使いたかったのですが、-sdk iphonesimulatorだとarchiveができないので諦めました。

lane :appium do
  xcbuild(
    project: 'TargetApp.xcodeproj',
    scheme: 'TargetApp',
    configuration: 'Debug',
    sdk: 'iphonesimulator',
    destination: 'platform=iOS Simulator,name=iPhone 6,OS=9.1',
    build_settings: {
      CONFIGURATION_BUILD_DIR: 'appium/apps'
    }
  )
  appium(
    app_path:  'appium/apps/TargetApp.app',
    spec_path: 'appium/spec',
    platform:  'iOS',
    caps: {
      versionNumber: '9.1',
      deviceName:    'iPhone 6'
    }
  )
end

例えばテストはこんな感じで、appium/spec下に置きます。

describe 'Sample' do
  it 'should display success' do
    find_element(:name, 'Show').click
    expect(find_element(:name, 'Success').displayed?).to be_truthy
  end
end

これだけでiPhone6のシミュレータでテストが走ります。(もちろん事前にnpm install -g appiumとかしておく必要があります)
結果はこんな感じです。

[12:35:38]: -------------------------------
[12:35:39]: Driving the lane 'ios appium' 🚀
[12:35:39]: --------------------
[12:35:39]: --- Step: appium ---
[12:35:39]: --------------------

+----------------------+---------------------------+
|            Summary for Appium Action             |
+----------------------+---------------------------+
| platform             | iOS                       |
| spec_path            | appium/spec               |
| app_path             | appium/apps/TargetApp.app |
| invoke_appium_server | true                      |
| host                 | 0.0.0.0                   |
| port                 | 4723                      |
| caps                 |                           |
+----------------------+---------------------------+

.

Finished in 1 minute 16.5 seconds (files took 22.59 seconds to load)
1 example, 0 failures

Appiumはテストのためにサーバーを起動しておく必要があるのですが、何も指定しなければアクション内で勝手に起動してテストが完了したら終了するようにしてあるため、laneを実行するだけで必要な構成が全て揃います。

自分でAppiumサーバーを起動しておいてテストだけ走らせたい場合は、invoke_appium_servertrueに設定することで起動処理をスキップします。
Appiumサーバーの起動コストが気になる場合などに使えます。

今後

fastlaneの本家にPull Request送ってるので、マージされたらfastlaneとAppiumを使ってる方はぜひ使ってみてください。
できればAndroidのテストも走らせられるようにしたいのですがAndroidの場合Emulatorの管理がちょっと面倒くさそうで、いったん保留しています。
あとは、Appiumには各言語バインディングがあってRuby以外でもテストが書けるのですが、実装的にRSpecでのテストに限定されるのもちょっと問題かもしれません。
いいアイデアあれば教えてください。(fastlane使う人はRuby使うよね、というのは甘えでしょうか)

宣伝

freeeではビジネス向けアプリに革命を起こすモバイルエンジニアを募集しています。よろしくお願いします。

明日はfreeeのフロントエンドにレボリューションを起こした革命家 @joe-re です。