画像比較を使ったE2Eテストの自動化

はじめに

三菱総研DCSのフロントエンド技術者の千葉です。
今回はブラウザテストの自動化でお馴染み、Seleniumを使ったE2Eテストの事例を紹介します。

Seleniumの概要

Seleniumとは、ブラウザ上でのWebページの操作を自動化するオートメーションツールです。
本来、人間が手で操作を行う必要のある、Webページの動作試験の自動化によく使用されます。

Seleniumを使うと、マウスやキーボードの操作を自動化できるので、画面操作→要素をチェック、の流れを繰り返してテストを行います。
要素のチェックとは、該当のHTMLタグの中に期待するテキストが表示されているか、といった形でチェックが可能です。

例: 下記、idがhelloのspanタグの中に「Hello World」と出力されているのかをチェック(異なる文字が表示される、タグ自体が表示されていない場合はテストNG)

<span id="hello">Hello World</span>

ほとんどのWebページはこの方法でテストができますが、私が携わった案件で通用しないケースがあり、今回紹介する画像比較に取り組むきっかけとなりました。

事例

基本的にほとんどのWebページは上記の方法でテストが可能ですが、私が携わった案件で試験するwebページが特殊で、GoogleMap等の地図上に様々な要素を描画するといったものでした。 例えば、銀行ATMの場所と利用状況を地図上に表示する、といった、地図に付加情報を表示するようなアプリケーションです。 地図はCanvasと呼ばれる要素の中に絵として描画されるため、HTMLタグをチェックする通常の仕組みでは、Canvas内に何が描画されているのか試験することはできません。

何か良い方法が無いか思い悩んだ末、Canvasは絵(画像)と同じなので、画像比較をすることで試験ができるのではないかと、取り組みを始めました。

画像比較を使った試験

仕組み自体は単純明快で、正しいテスト結果のキャプチャを事前に用意し、Seleniumでブラウザを動かしながらキャプチャを取得し、事前に用意したキャプチャと比較する、というものです。
人間と異なり、Seleniumは毎回全く同じ操作をエミュレートできるので、同一条件であれば毎回全く同じキャプチャが取得されます。
そこで、試験は1pxでも差があるとエラーとなるようにしました。

テスト(ブラウザ)を動かす方は通常のブラウザ試験と同様にSeleniumをそのまま使用し、テスト実行中に取得したキャプチャを比較するために、OSSのImageMagickを使用しました。
ImageMagickはWindows、Mac、Linux、幅広いプラットフォームで動作しますので、作成したテストを動作させることも簡単です。

画像比較のやり方

必要なツールはこの3つです。

ツール用途
Selenium事前に用意したテストコードを元にブラウザを自動操作
Node.jsSeleniumのテストコードを実行
ImageMagick画像を比較

Seleniumは様々な言語でテストコードの作成、実行ができるため、JavaScriptの得意な私はNode.jsを選択しました。

ImageMagickは端末にインストール後、コンソールを開いて下記コマンドを入力すると画像同士が比較できます。

compare -metric AE xxx.png yyy.png result.png

実際にこの2枚を比較すると、

このように差分のある個所だけ赤くハイライトされます。

node.js上からImageMagickを呼び出せるライブラリもあるため、テストコードへの組み込みも簡単です。

例:ライブラリを使った比較処理

gm().compare('./a.png', './b.png', options, (err, isEqual, equality, raw) => { if (equality !== 0) { console.log('差分があります'); } });

メリット

実際にテストに組み込んでみて、一般的なSeleniumによる自動試験と比較してこのようなメリットがありました。

  1. タグ以外の要素を試験することができる
    今回、これを取り組む切っ掛けとなったCanvas上の描画試験が可能となりました。他にもタグには反映されない画像等の試験にも活用することができます。

  2. テスト結果のチェック内容をテストコードに記載する必要がない
    画像同士を比較するだけなので、冒頭の例で示したように、タグのどの部分に何が表示されているのか、といったチェック内容をテストケースごとに書く必要がありません。キャプチャを取得する処理と比較する処理を共通化して埋め込むだけで試験が可能です。 テストの作成者はページをどのように操作してどの部分でキャプチャを撮るか、のみを意識すれば良くなります。 タグでチェックする必要がないため、タグの構成変化等にも強くなります。

デメリット

一方で、特に運用面ではデメリットがいくつかあります。

  1. キャプチャの取得タイミングがシビアな場合がある
    期待する描画が全て完了してからキャプチャを取得する必要があるため、ネットワークなどの外的要因に左右され易くなります。
    HTMLタグであれば描画の完了をSeleniumから確認する事ができますが、Canvas上の場合はSeleniumから検知ができません。今回のWebページはアプリ側からCanvasの描画完了を返す仕組みがあったため、それをテストコードから呼び出す事でテストが安定しました。
    そのような仕組みが無い場合は、描画が完了するまでのおおよその待ち処理を入れるといった対応が必要になりますが、あまり入れすぎるとテスト全体の時間が延びてしまいがちなので注意が必要です。

  2. 比較元の画像を事前に用意する必要がある
    テスト結果の比較元となる画像を事前に用意する必要がありますが、これのメンテナンスに苦労しました。
    比較元の画像自体はテストを一巡回すと取得することができますが、まず、その取得された画像が正しいかを人間がチェックしないといけません。数十枚であればそれほど手間ではありませんが、テストが増えてきてそれが100・・・1000ケースと増えていくと画像のチェックに時間を要してしまいます。
    もう1つ、ブラウザのアップデートが天敵です。ブラウザのバージョンが上がると、人間が見る分には気にならない程度の表示の違いが入る場合があり、その都度、比較元画像の更新が必要になります。特にChromeは、アップデートが6週間に1回入るため、毎回ではありませんが、表示が微妙に変わる事による比較元画像のメンテナンスが定期的に発生します。もちろんマイナス面だけでなく、ブラウザアップデートによる画面の崩れなどの検知にも役立つケースがありました。
    また、比較元画像の更新を最小限に抑えるため、画像を比較する範囲を試験が必要な範囲(地図部分)に限定する、等の工夫も取り入れています。想定外なところで、 Googleマップは左下にあるロゴのデザインが変わることがあり、それによるテスト差分が出てしまう事もありました。

まとめ

実案件での取り組みを振り返って、画像比較を使った試験は一定の効果を見込む事ができました。一方でメンテナンスコストがかかり易くなってしまうため、手放しでお勧めはできませんが、是非試してみたいテスト手法です。
今回の事例では全ての試験をこの方法で行ったため、メンテナンスコストがかかってしまいましたが、全体のうち、要所要所に組み込むようにすればコストもそれほど問題にならないかと思います。クリティカルな機能や頻繁に変更しない画面、表示崩れが絶対に許されないシステム、など、そういったシーンで活用していただくと効果的です。 また、メンテナンスコストに関してはなるべく簡略化できるように、案件内では画像更新作業を自動化するなど、様々な工夫に取り組んでいます。この仕組みを入れたアプリケーションをご担当されているお客様からは、この試験の仕組みが無くなったらアプリケーションのアップデートはもうできないくらいだと、評価をいただけました。 毎回リリース前の試験に工数を大きく取られている、試験で品質を担保しきれていない、等の課題をお持ちの方は是非お試しください。