WebdriverIOで始めるビジュアルリグレッションテスト

UI開発者 加藤

大規模なWebサイトの運用を長期間行っていると、ちょっとしたCSSの修正が思いもよらぬところに影響を与えてしまうことは少なくありません。しかし、更新するたびに全てのページを確認するのはとても大変ですよね。そんなときは「ビジュアルリグレッションテスト」が有効です。

リグレッションテストとはプログラムを変更したときに、予想外の影響が表れていないかを検証するためのテストのことで、そのテストを視覚的に、つまりキャプチャをとって行うテストのことをビジュアルリグレッションテストと呼びます。しかし、ビジュアルリグレッションテストを導入、運用するのはかなり大変で、チャレンジしたけど挫折した...という方もいらっしゃるのではないでしょうか。今日はそのハードルを取り除くべく、WebdriverIOを利用したビジュアルリグレッションテストの導入についてご紹介します。

WebdriverIOとは?

WebdriverIOはSelenium WebDriverを使用したクロスプラットフォームなテストフレームワークです。テストフレームワークなので基本的にはE2Eテストなどに利用しますが、単純にブラウザを自動操作したい場合にも利用することができます。WebdriverIOではServiceと呼ばれるサードパーティのパッケージや、テストの結果をどう出力するかを制御するReporterをインストールすることでかなり柔軟にテストの構築ができます。今回はWebdriverIO v5から使用できる「wdio-image-comparison-service」というServiceを利用してビジュアルリグレッションテストを行います。

※ Node.js v8系を利用しているとエラーになることが多いため、v10系にバージョンアップしておくことをオススメします。

環境の準備

WebdriverIOの基本的な準備を行います。まずはWebdriverIOのCLIパッケージをインストールします。

npm i @wdio/cli --save-dev

インストール後、テストを実行する環境などの設定を行います。下記のコマンドを実行するとQA形式で設定ができます。

npx wdio config

デフォルトで聞かれる質問の内容は下記のとおりです。(かっこ内はデフォルトの値です)

  • どこでテストを実行するか(local)
  • バックエンドはどこにあるか(On my local machine)
  • テストフレームワークは何を使用するか(mocha)
  • WevdriverIOのコマンドを同期、非同期どちらで実行するか(sync)
  • テストコードをどこに配置するか(./test/specs/*/.js)
  • テスト結果をどのように出力するか(spec)
  • 有効にしたいServiceはどれか(chromedriver)
  • テストしたいWebページのベースとなるURL(http://localhost)

全ての質問に答えると、wdio.conf.jsという設定ファイルが作成され、必要なパッケージがインストールされます。ビジュアルリグレッションテストを行うためのwdio-image-comparison-serviceはデフォルトではインストールされないので手動で追加します。またアサーションライブラリとしてChaiを使用するため、こちらも追加でインストールします。

npm wdio-image-comparison-service chai -D

つづいて、作成されたwdio.conf.jsに少し手を加えます。先ほどインストールしたwdio-image-comparison-serviceの設定です。

services: [
    ['image-comparison', {
        // 基準となる画像を保存するフォルダ
        baselineFolder: join(process.cwd(), './tests/sauceLabsBaseline/'),
        // 保存する画像のファイル名のフォーマット
        formatImageName: '{tag}-{logName}-{width}x{height}',
        // テスト実行時に保存される画像を保存するフォルダ
        screenshotPath: join(process.cwd(), '.tmp/'),
        // インスタンス毎に保存するフォルダを分けるかどうか
        savePerInstance: true,
        // 基準となる画像を自動で保存するかどうか
        autoSaveBaseline: true,
        // ブラウザのステータスバーを表示するかどうか
        blockOutStatusBar: true,
        // ブラウザのツールバーを表示するかどうか
        blockOutToolBar: true,
    }],
    'chromedriver'
],

このほかにもCSSアニメーションを無効にするオプションなどがあります。詳細はGitHubページの「Plugin options」の項をご覧ください。これで設定は完了です!次は、テストコードを書いていきます。

テストコード

今回のテストでは、実行するたびにWebページを上から下まで丸ごとキャプチャし、ベースキャプチャと実行時のキャプチャを比較して差分があるかどうかを確認します。ベースキャプチャとなるのは最初の1回目で保存されたキャプチャです。ベースキャプチャを変更する場合は手動でファイルを上書きします。

「環境の準備」フェーズで指定した「テストコードをどこに配置するか」で設定したディレクトリにJavaScriptファイルを配置します。

const expect = require('chai').expect;

describe('visual regression testing', () => {
    beforeEach(() => {
        // 対象となるページを開く
        browser.url('https://www.mitsue.co.jp');
    });

    it('should save some screenshots', () => {
        // フルスクリーンのキャプチャを保存する
        browser.saveFullPageScreen('fullPage');
    });

    it('should compare successful with a baseline', () => {
        // 基準となるキャプチャと比較して、差分がないことを確認する
        expect(browser.checkFullPageScreen('fullPage')).equal(0);
    });
});

ページを丸ごとキャプチャする必要がない場合は、単純に画面内だけ、もしくはCSSセレクタで一部の要素だけを指定してキャプチャすることも可能です。

// スクリーンに表示されている範囲を保存する
browser.saveScreen('screen');
// 要素を保存する
browser.saveElement($('.mv'), 'mainVisual');

// スクリーンに表示されている範囲を比較する
expect(browser.checkScreen('screen')).equal(0);
// 要素を比較する
expect(browser.checkElement($('.mv'), 'mainVisual')).equal(0);

以上でテストコードの作成は完了、あとは実行するだけです!

実行

準備が整ったら下記のコマンドを実行してみましょう。

npx wdio wdio.conf.js

デフォルトの設定であれば、Google Chromeが自動で立ち上がり、指定したページが開きます。1度目のテストでは、比較元となる基準のキャプチャがないため、その旨がコンソールに表示されます。2度目以降のテストでは、比較の結果が下記のように表示されます。差分を出すために当社のトップページサービスカテゴリのインデックスページを比較してみました。

expected 60.69 to equal 0

キャプチャを比較した結果、差分がない場合は0になるべきところが、6割くらいの差分が出ているということを指しています。(別のページなので、差分が出て当然ですね)差分がある場合はdiffフォルダが作成され、どこに差分があったかが分かる画像が保存されています。ピンク色になっている箇所が、比較の結果一致しなかった部分です。差分が一目瞭然ですね!

これまで私も「ビジュアルリグレッションテスト」と検索して何度も挫折してきましたが、これまで試したどのフレームワークよりも簡単にテストができたと思います。HTML ReporterJSON Reporterを使用すれば結果を見やすい形式で出力することもできます。ぜひ試してみてください!