LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログ

もっと気軽にリファクタリングするために、BackstopJSを導入した話

フロントエンド担当の岡田です。 ある程度の期間運用しているWebサイトの場合、CSSがカオスになりますよね。 ちょっとした修正が全体に影響してしまうのがCSSの怖いところです。

弊社でも、Sass化したり、共通のCSSを変更したりする度に主要ページを目視チェックしていました。 しかし目視チェックでは時間がかかりすぎますし、違いに気づけないこともあります。

そこで、BackstopJSを導入して、気軽にリファクタリングできる環境を作りました。

BackstopJSとは

BackstopJSは、CSSのリグレッションテストを自動化します。 具体的には、変更前・変更後の2つの画面のスクリーンショットを撮り、その差分を表示します。 f:id:lcl-engineer:20160625164434p:plain 左から順にリファレンス(変更前)、テスト(変更後)、差分の順に並んでいます。 差分のあるパーツは、ピンク色に塗られるので一目瞭然です。

BackstopJSでできること

  • ページ内の一部のパーツの比較(ページ全体の比較もできます)
  • 任意のパーツを除外したテスト(読み込みの度に表示が変わるものを除外できます)
  • スマホ・タブレットのテスト(レスポンシブサイトのみ)
  • SPAやAjaxで描画されるページのテスト

カスタムCasperJS scriptsを使うと以下のこともできるようになります

  • ユーザーエージェントを偽装したスマホ・タブレットのテスト
  • クリックしてから表示する箇所のテスト(まだ試していませんができるようです)

導入方法

以下のページでわかりやすく解説されているのでこちらでは割愛します。

ディレクター注目のBackstopJSを使用したリグレッションテストでバグ修正後の確認をしよう | 株式会社LIG
BackstopJSを使ったCSSのリファクタリング | フロントエンドBlog | ミツエーリンクス

弊社で使っている設定について

backstop.js

{
    "onReadyScript": "setUAiPhone.js",
    "viewports": [{
        "name": "sp",
        "width": 320,
        "height": 480
    }],
    "scenarios": [{
        "label": "http://localhost:3000/",
        "url": "http://localhost:3000/",
        "referenceUrl": "http://idou.me/",
        "hideSelectors": "iframe",
        "selectors": "body",
        "delay": 0,
        "misMatchThreshold": 0.1
    }, {
        "label": "http://localhost:3000/search/all/tokyo/osaka",
        "url": "http://localhost:3000/search/all/tokyo/osaka",
        "referenceUrl": "http://idou.me/search/all/tokyo/osaka",
        "hideSelectors": "iframe",
        "selectors": "body",
        "readyEvent": "backstopjs_ready",
        "delay": 0,
        "misMatchThreshold": 0.1
    }],
    "paths": {
        "bitmaps_reference": "../../backstop_data/bitmaps_reference",
        "bitmaps_test": "../../backstop_data/bitmaps_test",
        "compare_data": "../../backstop_data/bitmaps_test/compare.json",
        "casper_scripts": "../../backstop_data/casper_scripts"
    },
    "engine": "phantomjs",
    "report": ["CLI", "browser"],
    "cliExitOnFail": false,
    "casperFlags": [],
    "debug": false,
    "port": 3009
}

setUAiPhone.js

module.exports = function(casper, scenario, vp) {
  casper.echo("Setting UA");
  casper.then(function(){
    casper.userAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13F69 Safari/601.1');
  });
  casper.thenOpen(scenario.url);
}

※ setUAiPhone.jsは、casper_scripts/以下に保存します

解説

onReadyScript

ここでユーザーエージェントを変更するカスタムCasperJS scriptsを指定します

"onReadyScript": "setUAiPhone.js",

url

テスト対象ページ(変更後)を指定します

"url": "http://localhost:3000/",

referenceUrl

リファレンスページ(変更前)を指定します

"referenceUrl": "http://idou.me/",

hideSelectors

非表示にする要素を指定します。Twitter, Facebook, 広告などはiframeになっているので、iframeタグを指定しています

"hideSelectors": "iframe",

selectors

テストする箇所を指定します。bodyを指定するとページ全体をテストします

"selectors": "body",

readyEvent

SPAやAjax等、JavaScriptで描画される箇所があるページをテストする方法は2種類あります。1つはこのreadyEventを使う方法です。テスト対象ページの描画が完了したタイミングにconsole.log('backstopjs_ready');を追加します

"readyEvent":"backstopjs_ready",  

delay

SPAやAjaxのテスト方法のもう1つはこちらです。指定した時間だけキャプチャを取る時間を遅らせることができます。console.logを追加するのが面倒な場合は、delay設定が手軽です。ミリ秒単位で設定します。例:"delay": 1000, // 1000ミリ秒=1秒

"delay": 0,

misMatchThreshold

ミスマッチのしきい値。値が小さいほうが少しのズレでも差分として表示されます

"misMatchThreshold": 0.1

まとめ

BackstopJSは導入も簡単ですし、ローカルで動かせるので気軽にテストができます。 また、CasperJSを使うといろいろなことができそうなので、また新たな発見があれば共有します。

※ その後は、以下の仕組みも導入しています。

techblog.lclco.com