モバイル 自動化/自動テスト Advent Calendar 2017 14日目の記事です。
はじめまして、SWETグループの加瀬です(@Kesin11)
12/05に行われたiOS Test Night #6ではLT枠で発表させて頂きました。
今回は発表で紹介したrecordVideoについての補足と、UIテストを録画する方法を改めて紹介したいと思います。
以後の内容についての動作確認は以下の環境で行いました。
- Xcode 9.1
- Ruby 2.3.4
- Appium 1.7.1
recordVideoとは
xcrun simctl io
に含まれるiOSシミュレータの画面を録画するツールです。
XcodeのリリースノートによるとXcode 8.2より追加された機能です。
使い方はシンプルで、iOSシミュレータを立ち上げてから以下のコマンドを実行するだけです。
xcrun simctl io booted recordVideo ./test.mov
実行するとプロセスが立ち上がり、録画が開始されます。録画の終了はctrl+cです。
上記のコマンド中のbootedは「起動しているシミュレータ」という意味になります。以下のようにシミュレータのUDIDを指定することも可能です。
xcrun simctl io "FDF18984-04A9-4B9A-AD2C-E323152DFD03" recordVideo ./test2.mov
指定しているUDIDは自分の環境でのiOS 11.1、iPhone 5sのシミュレータのものです。
もう少し詳しいオプションなどはxcrun simctl io --help
で確認することができます。
recordVideoでUIテストの様子を録画する
recordVideo
を使ってiOSシミュレータの画面を録画することができたので、次はUIテストの開始と終了に合わせて自動的に録画の開始と終了を実行させてみましょう。
今回は以下のシェルスクリプトを作成して簡単に制御してみました。
recorder.sh
# recordVideoをバックグラウンドで実行 xcrun simctl io booted recordVideo screenshots/test.mov & # プロセスIDを保存 PID=`echo $!` # テスト実行 # この例ではAppium + RSpectでテストを実行しています bundle exec rspec spec/scenario_test.rb # バックグラウンドのrecordVideoにSIGINTシグナルを送信 kill -2 $PID
これだけでテストの起動と終了に合わせて録画ができるようになりました。
Appium + RSpecと組み合わせる
テストに合わせて録画することはできましたが、以下の点をまだ改良できそうです
- 録画するiOSシミュレータの指定がbootedなので複数のシミュレータが起動している場合は選択できない。UDIDを指定する場合は調べるのが大変
- UIテスト全体が録画されるため、失敗したテストケースが動画のどの部分か調べることが大変
これらの問題を解決するためAppium + RSpecでのテストに組み込むためのコードをRubyで書いてみました。
recorder.rb
require 'open3' require 'json' class Recorder attr_reader :udid, :pid, :file_path def initialize(driver) @udid = _get_udid(driver) end def _get_udid(driver) device_name = driver.caps[:deviceName] platform_version = driver.caps[:platformVersion] # appiumが実行しているシミュレータのUDIDを取得 stdout, _stderr, _status = Open3.capture3('xcrun simctl list --json devices') json = JSON.parse(stdout) booted_device = json.dig('devices', "iOS #{platform_version}").find do |device| device['state'] == 'Booted' && device['name'].match(device_name) end booted_device['udid'] end def start(file_path) raise 'UDID is null' unless @udid raise 'Already started recording' if @pid # バックグラウンドで録画を開始 @pid = spawn("xcrun simctl io #{@udid} recordVideo #{file_path}", out: '/dev/null', err: '/dev/null') @file_path = file_path Process.detach(@pid) end def stop raise 'Any recording process started' unless @pid # 録画終了 killed_process_num = Process.kill('SIGINT', @pid) raise "Kill pid: #{@pid} did not end correctly" unless killed_process_num.positive? # たまに終了に時間がかかる場合があるので待つ。既に終了している場合はエラーになるのでrescueで無視する begin Process.waitpid(@pid) rescue Errno::ECHILD end @pid = nil end def remove_video raise 'file_path is null' unless @file_path File.delete(@file_path) @file_path = nil end end
RecorderクラスはrecordVideoを扱いやすくするためのクラスです。start()とstop()で録画をコントロールするのと、Appiumのdriverから録画するシミュレータを特定しています。
シミュレータの特定はAppiumのcapabilityとインストール済みのシミュレータ情報を表示するxcrun simctl list
を組み合わせて実現しています。
まず、driverのcapabilityからdeviceName(端末名)とplatformVersion(iOSのバージョン)を取り出します。
xcrun simctl list
の実行結果にはシミュレータのUDIDが含まれているので、先程のdeviceNameとplatformVersionと照合することによりUDIDを得ることができます。
次はRecorderクラスを実際に使用するコードです。RSpecを使う場合には共通処理をヘルパーとしてテスト本体とは分離することが多いと思いますので、そこに組み込みます。
spec_helper.rb
capability = { caps: { # iPhone 5s, iOS 11.1のシミュレータの場合は以下を設定 platformVersion: '11.1', deviceName: 'iPhone 5s', # その他の設定は環境に応じて設定してください app: APP_PATH, platformName: 'iOS', automationName: 'XCUITest', }, appium_lib: { wait: 60 } } RSpec.configure do |config| config.before(:each) do |example| @driver = Appium::Driver.new(capability) @driver.start_driver @recorder = Recorder.new(@driver) @recorder.start("#{ROOT}/screenshots/#{example.description}.mov") end config.after(:each) do |example| @recorder.stop # テストが通った場合は録画を消す(=失敗したものは残る) @recorder.remove_video unless example.exception @driver.driver_quit end end
RSpecはRSpec.configureでbefore(:each)とafter(:each)に処理を仕込むことが可能なのでこれを利用しています。
before(:each)で録画を開始し、after(:each)で録画を終了することでテストケース毎に録画を分割しています。
さらにテストケースが無事に成功した場合には動画を削除することで、失敗したテストケースの動画だけがscreenshot/のディレクトリに最終的に残ります。
必要な処理はこれだけで、実際のテストケースを書くファイルには対応は不要です。
デモ
録画した動画のデモです。実際のUIテストはこの前後に別のテストケースが実行されており、失敗したこのテストケースの部分だけ動画が保存されます。
ちなみにこちらのデモはiPhone 5sのシミュレータで録画したものであり、25秒の動画で4.5MBのサイズでした。
まとめ
今回はiOS Test Night #6で発表したLTの補足という形でrecordVideoの紹介と、UIテストの様子を録画するTipsを紹介させて頂きました。
今回はAppium + RSpecでのUIテストと組み合わせてみましたが、recordVideo自体はRubyやAppiumに依存するツールではないため他にも活用方法が考えられると思います。
ぜひ色々試してみてください。