Androidのエンドツーエンドテストの自動化も Cucumber から Turnip へ

以前はAndroidのATDDにはCabash-Androidを入れてたのだけど、やっぱ時代はCucumberからTurnipだよねってことで、AndroidなTurnip環境を作ったので構築メモ。

ちなみに出来たものはこちらへ。

github - koduki/turnip_with_android_example

インストール

まずはこの辺参考にGenymotionを入れる。 別にGenymotionじゃなくて実機とかでも問題無いと思うけど、開発とかCIとかにはGenymotionが速くて便利。

インストールしたらデバックモードを有効にする。

Android 4.2以上はデフォルトでは開発者向けオプションがないらしいので

  • 「設定」=>「端末情報」=>「ビルド番号」を7回タップすると「設定」に開発者向けオプションが表示される
  • デバッグモード」をonにする

続いてappiumをnpm経由で入れる。

sudo npm install -g appium
sudo npm install wd

これで、実行環境の準備は完了。最後にTurnipの環境を作る。

まずはインストール用のディレクトリを作成.

% mkdir turnip_with_android_example/
% cd turnip_with_android_example
% mkdir spec/
% mkdir spec/acceptance/
% mkdir spec/steps/

続いて

% $EDITOR Gemfile

中身は下記の通り。

source "https://www.rubygems.org"
 
gem 'rspec'
gem 'selenium-webdriver'
gem 'turnip

Turnip用にfeatureを取り込めるように.rspecに下記を記載

% $EDITOR .rspec

中身は下記の通り。

-r turnip
% $EDITOR spec/spec_helper.rb
require 'bundler/setup'
require 'selenium-webdriver'

Dir.glob("spec/steps/**/*steps.rb") { |f| load f, true }

これで必要なファイルが出来たので、Bundlerで依存ライブラリをインストールする

% bundle install --path vendor/bundle

シナリオの作成

さて、ようやく準備が整ったところでシナリオの作成。

開発中のアプリとかをテストするのが本来だけど、サンプルとしてのポータビリティを加味して、デフォルトで入っている[Settting]を対象にテストを書いてみる。

まずはfeatureファイルを作成。

% $EDITOR spec/acceptance/settings.feature
Feature: 設定画面
  Scenario: 端末情報を表示する
    Given テスト対象は "android" 端末
    When "About phone" をタップする
    Then Android Version は "4.2.2" が表示されていること

一旦この時点で実行してみる。bundlerで環境作ってるのでbundler経由でrspecを実行するのがポイント

% bundle exec rspec

stepファイルが無いので下記のようなエラーが出るはずです。

設定画面
  端末情報を表示する
    テスト対象は "android" 端末 -> "About phone" をタップする -> Android Version は "4.2.2" が表示されていること (PENDING: No such step: 'テスト対象は "android" 端末')

Pending:
  設定画面 端末情報を表示する テスト対象は "android" 端末 -> "About phone" をタップする -> Android Version は "4.2.2" が表示されていること
    # No such step: 'テスト対象は "android" 端末'
    # ./spec/acceptance/settings.feature:3

続いてstepファイルを記述。中身は下記の通り。

# coding: utf-8

module SettingsStep
  step 'テスト対象は :device 端末' do |device|
    @device = device
  end

  step ':target をタップする' do |target|
    about = find_first_element('//text[@text="' + target + '"]')
    about.click
  end

  step 'Android Version は :expected が表示されていること' do |expected|
    version_setting = find_first_element('//list/linear[4]/relative')
    version_value = version_setting.find_element(:xpath, '//text[2]')

    expect(version_value.text).to eq(expected)
  end

  def find_first_element xpath
    #flick the screen until find the target item
    while driver.find_elements(:xpath, xpath).count == 0
      begin
        driver.execute_script 'mobile: flick', :startY => 0.9, :endY => 0.1
      rescue
      end
    end

    driver.find_elements(:xpath, xpath).first 
  end

  def driver
    case @device
    when 'android'
      desired_caps = {
            'browserName' => 'android',
            'platform' => 'linux',
            'version' => '4.1',
            'app-activity'=> '.Settings',
            'app-package'=> 'com.android.settings'
        }
      server_url = "http://127.0.0.1:4723/wd/hub"

      @driver ||= Selenium::WebDriver.for(:remote, :desired_capabilities => desired_caps, :url => server_url)
      @driver.manage.timeouts.implicit_wait = 3
    end
    @driver
  end

  def cleanup
    if @driver
      driver.quit
      @driver = nil
    end
  end
end

RSpec.configure do |conf|
  conf.include SettingsStep
  conf.after(:each) do
    cleanup
  end
end

基本的にはただのrsepcですね。1点注意としてはfind_first_elementの中でやってるみたいに画面上に表示されてない項目を出すためにスクロールする必要があります。 これ分からなくて、最初嵌った...

これを実行すると、下記の用にGreenな結果になる。

% bundle exec rspec

設定画面
  端末情報を表示する
    テスト対象は "android" 端末 -> "About phone" をタップする -> Android Version は "4.2.2" が表示されていること

Finished in 9.14 seconds
1 example, 0 failures

まとめ

とりあえずAndroidでturnipな環境が完成。割と便利そう。 ただ、構築が少し面倒なところも。npmとgem使うので、そっち系の親和性があってかつAnrdoidでJavaも書くぜーってエンジニアが居ないと、使うのはともかく導入に手間取るかなーという印象でした。国内情報少ないしね><

あと、cucumberの時もそうだったけど、Firebugとかに相当するDOMインスペクタが無いとstep書くのがちとしんどいのよね。 でも動画でそれっぽいものがappiumある的なことを言ってたので、調べてみようかな。

なんにしても後はテストをひたすら書くだけじゃー!! それではHappy Hacking!

参考