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

こんにちは、freeeのモバイルエンジニア @yonekawa です。
この記事は [freee Engineers Advent Calendar 2015](http://qiita.com/advent-calendar/2015/freee) 3日目です。

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

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

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

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

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

`Fastfile`には以下のように定義します。
本当はビルドに[gym](https://github.com/fastlane/gym)を使いたかったのですが、`-sdk iphonesimulator`だとarchiveができないので諦めました。

```rb
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`下に置きます。

```rb
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`とかしておく必要があります)
結果はこんな感じです。

```bash
[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_server`を`true`に設定することで起動処理をスキップします。
Appiumサーバーの起動コストが気になる場合などに使えます。

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

# 宣伝
freeeではビジネス向けアプリに革命を起こす[モバイルエンジニアを募集](http://jobs.jobvite.com/freee/job/om741fwi)しています。よろしくお願いします。

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