40通り以上の自動マルチブラウザテストをSelenium x CircleCI x BrowserStackで実現する

こんにちは。プレイドの@sdaikichiです。現在のトレーナーレベルは22、捕まえたポケモンは103種です。

前回の@positiveflatの記事ではプレイドにおける自動テスト環境をご紹介しました。

今回は、プレイドが

  • __40通り以上の環境__を対象に
  • __すべて自動__で
  • ついでに__CircleCIのコンテナリソースもそんなに使わない__で

マルチブラウザテストを実現している環境についてご紹介します。

目次

  • なぜマルチブラウザテストが必要なのか
  • BrowserStackの紹介
  • 運用してみてわかったコツ
  • まとめ
  • 最後に

なぜマルチブラウザテストが必要なのか

KARTEをお客様が利用する際には、来訪者をトラッキングしたり接客サービスを表示するためのscriptタグをお客様サイトに埋め込んでいただきます。

(接客サービス配信のイメージ)

お客様サイトには当然様々な環境(OS,ブラウザ,端末...)の来訪者がいらっしゃいます。

その際に特定のブラウザでのみ意図した挙動をしない、といった事態は当然避ける必要があります。
世のフロントエンドエンジニアの皆様はIE対応というワードを聞いただけで__仕事を放り投げて目黒川にミニリュウを捕りに行きたくなる衝動に駆られる__かと存じますが、KARTEではそうはいきません。

全ての環境をトラッキングや接客サービス配信の対象としているわけではありませんが、どんな環境でもScriptの実行はされるため、古いブラウザとはいえエラーの発生などは防ぐ必要があります。

例えば有名なのがこちら。この構文が紛れ込んでしまうだけでIE6,7でSyntaxErrorが発生してしまいます。

var data = { hoge:1, fuga:2, piyo:3, }; // オブジェクトリテラル末尾のカンマ

エンジニアが気をつけていても、例えば依存ライブラリの更新などでこういったエラーが入り込む可能性があります。
かといってこういったエラーにエンジニアが過度に敏感になると開発スピードが著しく落ちてしまいます。

そこで、これまであったトラブルや考えうるケースを抽象化し、CircleCI上でマルチブラウザのテストを行おう!ということになりました。

BrowserStackの紹介

マルチブラウザテストの実行にはBrowserStackAutomateを利用しています。

このサービスの特徴として

  • かなり自由度の高い組み合わせのOS/ブラウザ/端末が用意されており、設定ファイルに追記するだけで簡単にテスト対象を追加できます。

  • 管理画面も非常に充実しており、Selenium testではなんと 実行画面を全て録画し、動画として管理画面上で再生することができます
    これにより不便になりがちなCI環境のSelenium Testのデバッグが格段に行い易くなりますし、コマンドラインに表示されるテスト結果からはわからない各ブラウザでの細かい表示の違いなどにも気付きやすくなります。

  • Javascript Testing(ブラウザ上で行うユニットテスト)、Selenium Testing(Selenium webdriverを利用したUIのテスト)に対応しています。

テスト対象はブラウザ上で動作するJavascriptですが、メソッドごとのユニットテストはJavascript Test、接客サービスの表示・操作などUI的な部分はSelenium Testと、観点によりテストを分けています。

Javascript Test

BrowserStackのJavascript Testを利用するにあたり、様々なライブラリが公開されています。
https://www.browserstack.com/javascript-testing-api
プレイドではBrowserStack Runnerを利用しています。

BrowserStack Runnerはjsonの設定ファイルに

  • 実行するテスト対象ファイル
  • テストフレームワーク(mocha, jasmine, qunit)
  • 対象ブラウザ

などを指定するだけでテストを実行してくれるため、簡単に導入することができます。

jsonの設定ファイルを切り替えれば、たとえばサービスの対象ブラウザ用のテストと非対象ブラウザ用のテストを用意して、それぞれ別に実行することができます。

target.json

{
    "username": "yourusername",
    "key": "youraccesskey",
    "test_path": "target_browser_test.html",
    "test_framework": "mocha",
    "browsers":[
        { "os": "Windows", "os_version":"10", "browser": "Firefox" , "browser_version": "47"},
        { "os": "Windows", "os_version":"10", "browser": "Chrome", "browser_version": "51" },
        { "os": "Windows", "os_version":"10", "browser": "Edge", "browser_version": "13" }
    ]
}

no_target.json

{
    "username": "yourusername",
    "key": "youraccesskey",
    "test_path": "no_target_browser_test.html",
    "test_framework": "mocha",
    "browsers":[
        { "os": "Windows", "os_version":"10", "browser": "IE" , "browser_version": "7.0"},
        { "os": "OS X", "os_version":"El Capitan", "browser": "Safari", "browser_version": "9.1" },
        {"os": "ios", "os_version": "9.3", "device": "iPhone 6S"},
    ]
}

設定ファイルはBROWSERSTACK_JSONという環境変数を読みにいきます。
CircleCIではコマンド実行時の環境変数をcircle.ymlで簡単に指定することができるので、以下のように記述すればOKです。

test:
  override:
    browserstack-runner:
      BROWSERSTACK_JSON: target.json
    browserstack-runner:
      BROWSERSTACK_JSON: no_target.json

実行すると管理画面にはこのように結果が表示されます。

Selenium test

Selenium testは、自サーバに用意したテストページにアクセスして行っています。
このページにはお客様にサイトに埋め込んでいただくScriptが置かれており、実際の接客サービスの配信やエラーの有無などを確認することができます。

BrowsesrStackからローカル(この場合CircleCIのサーバー)にアクセスさせるためにBrowserStack Localをダウンロードし、実行しておきます。

dependencies:
  pre:
    - cd /usr/local/bin;
    sudo wget "https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip";
    sudo unzip BrowserStackLocal-linux-x64.zip;
    sudo rm BrowserStackLocal-linux-x64.zip;
test:
  pre:
    - /usr/local/bin/BrowserStackLocal youraccesskey:
      background: true

テストコードの中でBrowserStackを利用するようにselenium webdriverのcapabilityを指定します。

CAPABILITIES = [
  { os: 'Windows', os_version:'10', browserName: 'Edge'}
  { os: 'Windows', os_version:'10', browserName: 'IE', browser_version: '11.0' }
  { os: 'OS X', os_version:'El Capitan', browserName: 'Safari' }
  { os: 'OS X', os_version:'El Capitan', browserName: 'Firefox' }
  { browserName: 'iPhone', platform: 'MAC', device: 'iPhone 6S Plus', emulator: true } #emulator: trueの理由は後述
  { browserName: 'iPhone', platform: 'MAC', device: 'iPhone 6S', emulator: true}
]

CAPABILITIES.forEach (capability) ->
  before ->
     @driver = new webdriver.Builder()
        .usingServer('http://hub-cloud.browserstack.com/wd/hub')
        .withCapabilities(capabilities)
        .build()
  #以下テストケースを記述。
  #ローカルへのアクセスが発生する部分のURLはlocalhostでOK

運用してみてわかったコツ

BrowserStackでのテスト並列化

前回の記事 ではCircleCIのコンテナ追加による並列化・高速化について触れました。
BrowserStackのSeleniumテストも、ローカルやCIで行う場合と同様、時間がかかります。
しかしながらBrowserStack AutomateのSelenium Testは実行はBrowserStackのサーバ上で行われるため、手元のリソース(CircleCiのコンテナ)は必要ありません。

逆に言いますと、ボトルネックになるのはBrowserstack側の並列上限数です。
1〜25の並列数でPricingが用意されています 。サービス規模やリポジトリへのpush頻度にあわせて選ぶと良いでしょう。

プレイドでは契約した並列数に合わせてCircleCI上でテストを__すべてバックグラウンドで実行する__shell scriptを記述し、circle.yml上で実行しています。

Mobileは自由度低め

Mobileのテスト環境はPCに比べ、自由度が若干落ちます。
PCがOS/Browser、それらのバージョンを自由に組み合わせて使えるのに対して、Mobileは端末/OS/Browser及びそれらのバージョンは固定です。それでも圧倒的なバリエーションですが。
また、最近エミュレーターではなく実機でのテストがリリースされたようですが、こちらを試してみたところまだ動作が不安定だったため、プレイドでは導入を一旦見送っています。(これがSelenium Testでのemulator:true の理由)

Retry機構は必要

BrowserStackではたまにインスタンス・エミュレータの起動に失敗することがあるようで、1ケースも実行されずタイムアウトで落ちることがあります。
そのため、プレイドでは各環境のテストが失敗した際には3回までのRetryを行っています。
ここは開発速度とのトレードオフで現在も試行錯誤中です。

まとめ

以上、プレイドにおけるマルチブラウザテストについてご紹介いたしました。
{ os: 'OS X', os_version:'Snow Leopard', browserName: 'Firefox' } のような、シェアの低さゆえにもし問題が起こるとしたらかなり気付くのが遅れそうな環境でも手軽にテスト対象に加えられるようになったのは大きいと思います。

もちろん、まだまだテストケースをはじめテスト環境も改善を加えていきます。

ウチはこんなことやってるよ!といった事例がございましたらぜひぜひシェア時にコメントくださいませ!

最後に

ウェブ接客プラットフォーム「KARTE」を運営するプレイドでは、 KARTEを支える技術に興味を持つエンジニア(インターンも!)を募集しています。

詳しくはこちら(Wantedly)をご覧ください。 もしくはお気軽に、こちらの「話を聞きに行きたい」ボタンを押してください!