BackstopJSでビジュアルリグレッションテスト！

BackstopJSでのビジュアルリグレッションテストをした際のまとめです。
勉強中のため、コメント・アドバイスをいただけると幸いです。

# ビジュアルリグレッションテストとは
ビジュアルリグレッションテストは、CSSやコードを変更した後に、変更前と同じ表示・実行結果になるかを検証するテストの一種です。
スクリーンショットを撮影することで、前後で差分が出ていないか検証します。

### 検出できるもの
* ボタンや画像が表示可否
* ページの表示可否

### 検出できないもの
* ボタンがクリック
* ページ遷移
* 画像のalt属性設定可否

# BackstopJSでのビジュアルリグレッションテスト
今回は、BackstopJSでビジュアルリグレッションテストを行います。

## BackstopJSの使い方
(1) Node.js / npm をインストール
(2) npmでBackstopJSをインストール
```$ install -g backstopjs```
(3) BackstopJSプロジェクト用のディレクトリを作成し以下を実行
```$ backstop init```
(4) 変更前のスクリーンショット（Reference）を保存 
```$ backstop reference```
(5)　変更後のスクリーンショット（Test）を保存
```$ backstop test``` 
(6) 両者を比較したレポート生成されます

### backstop.jsonの基本
backstop.jsonの中身は以下のようになっています。
テストシナリオは、backstop.jsonの項目に設定していきます。
必須の項目は、id, viewports, scenarios.label , scenarios.urlの4つです。
最低限、この4つを設定することでテストを実行できます。

```
{
  "id": "backstop_default", // [必須]テストのタイトル設定
  "viewports": [ // [必須]画面サイズ設定
    { // スマートフォン画面サイズ
      "label": "phone",
      "width": 320,
      "height": 480
    },
    { // PC画面サイズ
      "label": "tablet",
      "width": 1024,
      "height": 768
    }
  ],
  "onBeforeScript": "puppet/onBefore.js", // ブラウザの設定(Puppeteer利用時)
  "onReadyScript": "puppet/onReady.js", // スクリーンショット前のUI状態変更(Puppeteer利用時)
  "scenarios": [ // テストシナリオ
    {
      "label": "BackstopJS Homepage",　// [必須]テストシナリオ名
      "cookiePath": "backstop_data/engine_scripts/cookies.json", // cookiesのPATH
      "url": "https://garris.github.io/BackstopJS/", // [必須]画面撮影URL
      "referenceUrl": "", // 変更前の画面撮影URL
　　　　：
    }
  ],
```

# テストのカスタマイズ方法

実際にテストを行う場合は以下のような要望が出てくると思います。

1. ベーシック認証のある環境でも使いたい。
2. 環境ごとに変わる文言にダミー画像を差し込みたい。
3. テストシナリオを複数分割したい。

それらに対応する方法を説明したいと思います。

## ベーシック認証のある環境でも使う方法
BackstopJSのonBefore.jsに、ブラウザのベーシック認証処理を設定することができます。
これは、[「BackstopJSでProxyを通したり、ベーシック認証を通したり。」](https://qiita.com/Tak_Matz/items/8980d31d3192178e4cec) の方法を参考にさせていただきました。

Puppeteerを利用する場合
```backstop_data/engine_scripts/puppet/onBefore.js```
ChromyJSを利用する場合
```backstop_data/engine_scripts/chromy/onBefore.js```

## 環境ごとに変わる文言にダミー文言を差し込む方法
環境やアクセスのタイミングで表示が変わる文言や画像があります。
文言が違うので差異があると検出されてしまいますが、今回検出したい表示崩れの差異ではないので、
検出されないようにしたいです。そのためにダミー文言を差し込みます。

**文言が違いによる差異検出**
<img width="500" alt="文言が違いによる差異検出" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313327/81d0ac22-1682-c24d-cc55-f0b0b518e281.png">
↓
**ダミー文言差し込み**
<img width="500" alt="ダミー文言差し込み" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313327/4c4ac134-185b-de42-5f5e-79e673953047.png">

これは、onReadyScriptで実現できます。
BackstopJSのonReadyScriptでは、スクリーンショット前のUI状態変更することができます。
これを利用し、ダミー文言を差し込むことができます。
今回は、Puppeteerで要素の操作を行いました。

以下のindex.htmlのタグの文言を差し替えたい場合は、onReady.jsを以下のように書きます。

```index.html
<span class="tag"><a href="">関係</a></span>
```

```onReady.js 
module.exports = async (page, scenario, vp) => {
    page.evaluate(function () {
        var tagList = document.getElementsByClassName("tag");
        for (var i = 0; i < tagList.length; ++i) {
            tagList[i].innerHTML = "<a href=\"\">片思い</a>";
        }
    });
};
```

## テストシナリオをbackstop.jsonとは別に管理する
テストシナリオは、backstop.jsonのscenariosに書くのですが、
シナリオの数が増えていくと、backstop.jsonが巨大になってしまいます。
なので、テストシナリオをGoogle スプレッドシートで管理できるようにしました。
Google スプレッドシートを選んだ理由は、ディレクター等の非エンジニアの方にもテストの内容を共有しやすかったからです。

BackstopJSは、以下のようにモジュールとして呼び出すことができます。

```runner.js
const backstop = require('backstopjs');
```

モジュールとして呼び出せるので、スプレッドシートのデータを読み込み、BackstopJSを実行するスクリプトを作成することができます。
これは、[BackstopJS を利用した Visual Regression Testing の感想と工夫ポイント](http://upinetree.hatenablog.com/entry/2018/05/13/231951) の方法を参考にさせていただきました。

## まとめ
ビジュアルリグレッションテストは、導入コストの低いテストだと思います。
すべてをテストできる訳ではありませんが表示崩れの検出はできるので、
テストのない環境で表示崩れが怖くリファクタリングができていない方はぜひお試しください。


# 参考文献
+ [Awesome Visual Regression Testing](https://github.com/mojoaxel/awesome-regression-testing)
+ [BackstopJS](https://github.com/garris/BackstopJS)
+ [BackstopJS を利用した Visual Regression Testing の感想と工夫ポイント](http://upinetree.hatenablog.com/entry/2018/05/13/231951)
+ [1pxの変化も見逃さない！ビジュアルリグレッションテスト導入で快適フロントエンド開発](https://tech.dely.jp/entry/vis_reg_test)
+ [もっと気軽にリファクタリングするために、BackstopJSを導入した話](https://techblog.lclco.com/entry/2016/06/30/232235)
+ [BackstopJSをMagento2のUIレグレッションテストに導入してみた。](https://qiita.com/Taku2/items/82007348cb39cc130cc4)
+ [BackstopJSでProxyを通したり、ベーシック認証を通したり。](https://qiita.com/Tak_Matz/items/8980d31d3192178e4cec)