1. 背景
Railsで作っているWebサービスのスマホWeb版を提供することになり、サポートする端末も決まった。しかし、
- サポート端末は毎年のように変わる可能性が高い
- サポート端末は随時追加される可能性が高い
などの理由から、もし実機で見たときの画面の見栄えが正しいかを手動でテストするとしたらアホらしいね、ということになり、せめて画面キャプチャをとるスクリプトを書こうということになった。
デプロイ前に毎回行うテストを用意するというよりかは、たまにチェックをしたいときに実行するようなrakeタスクにしたいと思い、調べたり検証した結果、
をrakeタスクから使うことにした。 ちなみに、実機での実際の見栄えのチェックじゃなくて、特定のhtmlの要素が存在するかどうかなど、自動化しやすい点をチェックしたいのであれば、rakeタスクで画面キャプチャを取るのではなく、上記の3つを使って普通にテストを書けば良いと思われる。
2. 開発環境構築
最終的には実機での確認をしたいが、実機を買ったりレンタルしたりする前に、iOSのSimulatorやAndroidのEmulatorでスクリプトの動作確認まではしておこう、ということで、一旦、端末のエミュレータを使う前提で環境を作っていく。ブログエントリーのタイトルは、「スマホ実機での」と書いちゃってるが、まあいいか。macOSを使う前提にする。
2.1. Xcodeのインストール
Appium ServerがiOS系の端末と接続するときに、Xcodeの機能に依存していたりするので、Xcodeをインストールしておく。Mac App Storeからインストールするだけ。
2.2. Android Studioのインストール
Android Studioの公式サイトのインストール手順に従って、インストールする
インストール後に、適当なプロジェクトを1つ作る
(プロジェクトを作らないとSDK managerとAVD managerをAndroid Studioのメニューから使えないため)[Tools -> SDK manager]を確認し、SDKやPlatform toolsなど必要なものが入っていなければ追加しておく。
[Tools -> AVD manager]から、新しいバーチャル端末を1つ適当に作っておく。
ここで作ったバーチャル端末を後でAndroid Emulatorとして使う。adbなど、あとでコマンドラインからも使えるようにしておきたいので、
ANDROID_HOME
とPATH
を以下のように設定しておく。なお、ANDROID_HOMEの場所は、[File -> Ohter Settings -> Default Project Structure]で開く設定画面や、SDK managerの画面のAndroid SDK locationに記載されている。 .bash_profileに以下を追加。
#Androidの設定 export ANDROID_HOME=$HOME/Library/Android/sdk export PATH=$PATH:$ANDROID_HOME/platform-tools
設定後は、コマンドラインでadbが実行できるか確認しておく。
$ source ~/.bash_profile $ adb
2.3. Appium Desktopのインストール
Appiumはnodeのnpmでもインストールできるが、
- Appium Desktopは色々環境構築が楽そう
- スマホアプリの要素を特定するためのインスペクタがAppium Desktopのものが便利そう
ということもあり、Appiumの公式チームが開発に関わっていると思われるAppium Desktopをインストールする。
公式サイトのリリースページから、dmgファイルをダウンロードして、普通にインストールするだけ。
2.4. Appium DesktopのJAVA_HOMEとANDROID_HOMEを設定
Appium serverが、Androidと接続する際に、内部的にadbコマンドを使うらしく、そういった処理のために、ANDROID_HOMEを設定しておく必要があるっぽいので、設定しておく。Appium Desktopを立ち上げた後、[Edit Configurations]ボタンから設定できるので、設定して保存しておく。
なお、JAVA_HOMEは、Android Studioをインストールしたときに付属してインストールされたものを使うことにする。普段使っているJDKがどこかにあれば、それを指定してもOKだと思われる。(が、自分はJavaをインストールしていないので、Android Studioのものにした。)
Android Studioをインストールしたときに、付属してインストールされたJavaは、Android Studioで[File -> Other Settings -> Default Project Structure]で開く設定画面に記載されているので、それを確認しておく。
これで、環境構築は終わり。
3. rake タスクの開発
ここからはrakeタスクの中身の実装について説明していく。
3.1. Gemfileに必要なライブラリを追加
以下をGemfileに追加
gem 'capybara' gem 'appium_lib' gem 'selenium-webdriver' gem 'appium_capybara' gem 'site_prism'
3.2. ソースコード用のディレクトリの作成
今回は、以下のようにディレクトリを使うことにした。
lib └── tasks └── mobile_view_check_task ├── devices_config │ ├── local │ └── production ├── mobile_view_check.rake └── pages
device_configs
以下には、環境ごと、デバイスごとのAppiumのcapabilitiesを定義したテキストファイル(後述)を置いていく。
pages
ディレクトリ以下に、site_prismで作ったPageオブジェクト用のファイルを置いていく。
mobile_view_check.rake
は、タスクを定義するためのファイル。
3.3. site_prismのページファイルを作成
実際仕事で使ったファイルは、ここには書けないので、Google検索ページを表示するコード例で説明しておく。
以下のような感じで、site_prismのページクラスを作っておく。 google_search.rb
require 'site_prism' require 'capybara' require 'capybara/dsl' class GoogleSearch < SitePrism::Page set_url('https://www.google.com') element :search_word, 'input[name="q"]' end
site_prismの詳しい使い方は、この記事では省略。公式サイトの説明が詳しいので、知りたい人は見ると良い。
3.4. Appiumで接続するcapabilitesを記載した設定ファイルを作成
iphone_simu_8plus.iniとして、以下のようなini形式のファイルを作成して、devices_config/local
ディレクトリ以下に配置。検証中は、理由は忘れたけど、waitを長めに設定していた。安定して動くようになったらもっと短くしても良いと思われる。
[caps] platformName = "iOS" deviceName = "iPhone 8 Plus" automationName = "XCUITest" platformVersion = "12.1" browserName = "Safari" locale = "ja_JP" language = "Japanese" [appium_lib] wait = 60 server_url = "http://localhost:4723/wd/hub"
同様に、Android Emulatorのiniファイル(android_emu.ini)は、以下。
[caps] platformName = "Android" deviceName = "Android Emulator" automationName = "Appium" browserName = "Chrome" nativeWebScreenshot = true [appium_lib] wait = 60 server_url = "http://localhost:4723/wd/hub"
最初、nativeWebScreenshot = true
を指定していなかったとき、以下のようなエラーが出て、画面キャプチャを撮れなかったので、追加した。
WebDriverError: An unknown server-side error occurred while processing the command. Original error: Could not proxy. Proxy error: Could not proxy command to remote server. Original error: Error: ESOCKETTIMEDOUT"
appiumで指定可能なcapabilitiesの一覧は公式サイトを見るのが良いと思われる。ハマったときに、ここをちゃんと読むと解決できることが多い。
ちなみに、Macで接続可能なiOS系のデバイスの一覧は、instruments
コマンドで以下のようにすると出力することができる。
$ instruments -s devices
同様に、Androidの端末一覧は、adb
コマンドで、以下のようにすればOK。
$ adb devices
3.5. rakeタスクの実装
main処理部分は、以下のようにして、iniファイルの数分だけ、デバイスごとにキャプチャを撮っていく。
namespace :mobile do desc 'スマートフォン対応ページに対して、自動的にスクリーンショットを取るタスク' task get_captures: :environment do device_config_dir = Rails.root.join('lib', 'tasks', 'mobile_view_check_task', 'devices_config').to_s @mobile_captures_root = Rails.root.join('mobile_captures').to_s capybara_setup env = ENV['env'] Dir.glob("#{device_config_dir}/#{env}/*.ini").each do |config| device_setup(config) google_search device_teardown end end # --- 途中省略 ---- # end
以下、省略部分の説明。
capybara_setupメソッドには、全体で一回設定しておきたいcapybaraの設定を記載。
def capybara_setup Capybara.configure do |config| config.run_server = false config.default_max_wait_time = 60 # テストしたいwebサイトのホストを環境変数CAPYBARA_APP_HOSTに定義 # dotenv-railsを使っている人であれば、.envファイルに定義する # これを定義しておけば、site_prismのset_urlを相対パスで記載可能 config.app_host = ENV['CAPYBARA_APP_HOST'] end end
デバイスごとのセットアップとお片付けメソッドを定義。あと、screenshotを保存するユーティリティメソッドも作っておいた。
def device_setup(config_file) @device_name = File.basename(config_file, '.ini') Capybara.register_driver(@device_name.to_sym) do |app| opts = Appium.load_appium_txt file: config_file Appium::Capybara::Driver.new app, opts end Capybara.current_driver = @device_name.to_sym end def device_teardown Capybara.current_session.driver.quit end def save_screenshot(file_name) # スクリーンショットをとるのが早すぎて、失敗することが多いので、スクリーンショットを取る前は、2秒待つことにする。 sleep(2) driver = Capybara.current_session.driver save_dir = "#{@mobile_captures_root}/#{@device_name}" # ディレクトリがなければ作っておく FileUtils.mkdir_p(save_dir) unless FileTest.exist?(save_dir) driver.browser.save_screenshot("#{save_dir}/#{file_name}") end
def google_search search_page = GoogleSearch.new search_page.load search_page.wait_until_search_word_visible save_screeenshot('google_search.png') end
最終的には、以下のようなファイルたちができている状態になる。
lib └── tasks └── mobile_view_check_task ├── devices_config │ ├── local │ │ ├── android_emu.ini │ │ └── iphone_sim_8plus.ini │ └── production ├── mobile_view_check.rake └── pages └── google_search.rb
3.6. rakeタスクの実行
実行すると、app_root/mobile_capturesディレクトリ以下に、デバイスごとのキャプチャ画像がどんどん作られていくので、先に.gitignoreには以下のようなやつを記載しておいたほうがよい。
.gitignore
# モバイルテストのキャプチャ画像は無視する /mobile_captures/*.jpg /mobile_captures/**/*.jpg /mobile_captures/*.png /mobile_captures/**/*.png
実行は、rakeファイルで書いた内容にしたがって、以下のように行う。 envで指定したディレクトリ以下の.iniファイルが使われるので、実行環境やその他の理由で使うデバイスを切り替えたい場合に、ディレクトリを分けておけばよい。自分は、local環境ではSimulatorとEmulatorだけを使うので、localディレクトリに.iniファイルを配置した上で、env=localを指定している。
実行前に、Android Emulatorは自分で立ち上げておかないといけない。Android StudioのAVD managerから立ち上げておく。
$ rake mobile:get_captures env=local
3.7. 実行後の結果確認
実行すると以下のように、エミュレータがうにょうにょ動き出し、mobile_captures以下にデバイスごとの画像が保存されていくので、画像をさっと確認すれば、目視による見栄えチェックが楽にできる。