BackstopJSを使用したCSSの回帰テスト

# 1. はじめに
Catch CSS curve balls.
CSSの回帰テストツールである[BackstopJS](https://github.com/garris/BackstopJS)を使用した際のメモです。
Githubのサンプルに加え、CasperJSで画面を操作した上での疎通も行っています。

# 2. 環境構築
検証環境はUbuntu15.04を使用しています。
そのほか下記必要となりますので、インストールされていないものについては適宜導入してください。

### gulp.jsのインストール

```
$ sudo npm install -g gulp
$ gulp -v
> CLI version 3.9.1
> Local version 3.9.1
```
### PhantomJSのインストール

```
$ sudo npm install -g phantomjs
$ phantomjs -v
> 2.1.1
```

### CasperJSのインストール

```
$ sudo npm install -g casperjs
$ casperjs --version
> 1.1.0-beta5
```
### BackstopJSのインストール
テスト用のディレクトリで、下記コマンドを実行します。

```
$ npm install --save-dev backstopjs
```
上記により、BackstopJS用のディレクトリ`./node_modules/backstopjs`が作成されます。

# 3. テスト
## 3.1. 設定ファイルの作成
`./node_modules/backstopjs`にて下記コマンドを実行します。

```
$ gulp genConfig
```
デフォルトの設定では、設定ファイル`backstop.json`はテスト用のディレクトリ直下に作成されています。
今回はサンプルとして下記のようなファイルを作成しました。
詳しい設定内容については、本家のGithubを参考にしてください。基本的には`viewports`と`scenarios`部分を修正することになるかと思います。

```backstop.json
{
  "viewports": [
    {
      "name": "pc",
      "width": 500,
      "height": 300
    }
  ],
  "scenarios": [
    {
      "label": "Sample page",
      "url": "http://localhost:8000",
      "hideSelectors": [],
      "removeSelectors": [],
      "selectors": [
        "body",
        "#button_id"
      ],
      "readyEvent": null,
      "delay": 500,
      "misMatchThreshold" : 0.1,
      "onBeforeScript": "",
      "onReadyScript": ""
    },
    {
      "label": "Hover test",
      "url": "http://localhost:8000",
      "hideSelectors": [],
      "removeSelectors": [],
      "selectors": [
        "#button_id"
      ],
      "readyEvent": null,
      "delay": 500,
      "misMatchThreshold" : 0.1,
      "onBeforeScript": "",
      "onReadyScript": "onReady.js"
    }

  ],
  "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": 3001
}
```

## 3.2. シナリオの作成
今回は下記のようなページのテストを実施します。

```index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="./sample.css">
<title>Sample</title>
</head>
<body>
  <button id="button_id" class="button-class">Button</button>
</body>
</html>
```
```sample.css
body {
    background: #456bd1;
}

.button-class {
    background: #456bd1;
    color: #FFFFFF;
    border: 1px solid #FFFFFF;
    font-size: 30px;
    width: 150px;
    height: 100px;
    border-radius: 10px 10px 10px 10px;
}

.button-class:hover {
    background: #FFFFFF;
    color: #456bd1;
}
```
初期表示
![webpage.png](https://qiita-image-store.s3.amazonaws.com/0/116002/ba063a64-1cac-a23d-fda7-38d08ec52963.png)
ボタンにマウスカーソルを合わせると
![hover.png](https://qiita-image-store.s3.amazonaws.com/0/116002/25a318a9-0af2-60ca-09b1-b1da1721b87e.png)

テストシナリオとしては、ページ全体の差分が発生していないかの確認と、ボタン部分の差分が発生していないかの確認の2つのシナリオを作成します。

### ページ全体の差分確認シナリオ
```json
    {
      "label": "Sample page",
      "url": "http://localhost:8000",
      "hideSelectors": [],
      "removeSelectors": [],
      "selectors": [
        "body",
        "#button_id"
      ],
      "readyEvent": null,
      "delay": 500,
      "misMatchThreshold" : 0.1,
      "onBeforeScript": "",
      "onReadyScript": ""
    },
```
### ボタン部分の差分確認シナリオ
```json
    {
      "label": "Hover test",
      "url": "http://localhost:8000",
      "hideSelectors": [],
      "removeSelectors": [],
      "selectors": [
        "#button_id"
      ],
      "readyEvent": null,
      "delay": 500,
      "misMatchThreshold" : 0.1,
      "onBeforeScript": "",
      "onReadyScript": "onReady.js"
    }
```
ボタン部分の`:hover`が正しく適用されているか確認したいので、CasperJSでマウスオーバーさせるような処理を記載します。
デフォルトで作成される`onReady.js`にマウスオーバーの処理を加えました。

```onReady.js
module.exports = function(casper, scenario, vp) {
  casper.echo('onReady.js', 'INFO');
  casper.evaluate(function(){
    console.log('This custom script is running inside your web app!');
    console.log('Add your own casper commands here to simulate user interacions or logging in.');
  });
  casper.mouse.move("#button_id"); //ボタン部分にマウスオーバー
  casper.wait(50);
};
```

## 3.3. 比較用画像の作成
`./node_modules/backstopjs`にて下記コマンドを実行します。

```
$ gulp reference
```

これで比較元になる画像一覧が`./backstop_data/bitmaps_reference`に作成されます。
この後はWebページの変更を加えた後にテストを実施しましょう。

## 3.4. テストの実施
ボタン部分にmarginを10pxずつつけてみたので、差分がでるかテストしてみます。

`./node_modules/backstopjs`にて下記コマンドを実行します。

```
$ gulp test
```

## 3.5 テスト結果の確認
テスト実施後、デフォルトで確認用のサーバが裏で立ち上がっているので、ブラウザ上`http://localhost:3001/compare/`で確認してみましょう。
ボタンの位置をずらしたので、画面全体ではテスト結果がFailedとなっています。ボタン部分については変更を行っていないため、Passedとなりました。

![result.png](https://qiita-image-store.s3.amazonaws.com/0/116002/c93a40fa-6635-4cdc-b551-e6a938416470.png)

# 4. 所感
動的な要素（アニメーションなど）についてのテストは難しいですが、基本的なCSSの見栄え等については、BackstopJSで比較的簡単にテストが実施できるかと思います。今回はマウスオーバーのサンプルでしたが、`casper.mouse.click()`で要素のクリック処理も可能なので「ボタンを押した後のUIの差分確認」というのも実施できます。
あと今回はPhantomJSを使用しましたが、エンジン部分にSlimerJSを代わりに用いることも可能です。

同じような思想のツールとしてPhantomCSSがあるので、また時間をみつけてさわってみようかと思います。