ビジュアルのRegression Test(回帰テスト)ツールを試してみた

運用年数の長いウェブサービスでcssやhtmlのリファクタリングをするにあたって、ビジュアルのRegression test(回帰テスト)が必要となってきたので、導入するにあたりいくつかのツールを眺めてみた。

今回、ビジュアルのRegression testとしてcsscritic、BackstopJS、Succss、PhantomCSSを軽く触ってみた。

csscritic

A lightweight tool for regression testing of Cascading Style Sheets

とある通り、csscriticはシンプルな設定でテストを実施することが可能。

1
npm i csscritic http-server --no-optional

テスト対象となるhtml(test.html)を以下のようにする

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
<head>
<title>Page under test</title>
<style type="text/css">
.test {
margin: 0;
padding: 0;
vertical-align: top;
width: 204px;
height: 50px;
font-size: 90px;
color: red;
}
.blue {
background-color: blue;
}
</style>
</head>
<body class="test">
<div class="test blue">
Blue
</div>
</body>
</html>

テスト用のhtmlファイル(RegressionRunner.html)を作成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html>
<head>
<title>Regression Runner</title>
<meta charset="utf-8"/>
<script src="./node_modules/csscritic/dist/csscritic.allinone.js"></script>
<script>
window.onload = function() {
csscritic
/* テストケースをlabel */
.component('Example test')
/* レポーターの設定 */
.add({
url: 'test.html',
desc: 'should look good'
})
/* テストケースを追加 */
.addReporter(csscritic.NiceReporter())
/* テスト実行 */
.execute();
};
</script>
</head>
<body>
</body>
</html>

APIはドキュメントがあるので必要に応じて参照。

RegressionRunner.htmlをブラウザで開くことでまず現在の表示状態を確認できる。
Chromeの場合same-originの制限で表示できないので、ローカルサーバーを立てて表示する。

1
2
node_modules/.bin/http-server
open http://localhost:8080//RegressionRunner.html

テスト対象のhtmlをPhantomJSでレンダリングし、対象となる箇所を目視確認し、問題ない場合はacceptしていく。(htmlファイルの他に画像ファイルを指定することも可能)

このacceptした結果はローカルのIndexed DBに保持される。
再度ページを読込むと、前回レンダリング結果との比較を行う。ここで差分がある場合はfaildとなる。

sama-originの制限や動的生成される場合などで、ローカルのコードベースでテストをできない時は、
proxyを使うか、screenshotの画像と比較するなどで対応すると良いらしい。

csscriticは簡単な設定で容易にRegression テストを導入できる。
Styleguideなどのドキュメントに導入するなどの活用ができそう。
基本的にローカルのstaticなコードに対してテストするので、それ以外の場合はひと手間かかる。

BackstopJS

BackstopJSは複数のview portに対してRegressionテストを実行できるのが特徴。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# インストール
npm i -D backstopjs
npm i -g gulp phantomjs casperjs
# backstopjsのGulp Taskを実行する必要があるため
cd node_modules/backstopjs
# configファイル(backstop.json)生成
gulp genConfig
# bitmaps_reference に比較元になる現在のキャプチャを生成する
gulp reference
# 変更した内容との比較がbitmaps_test/<timestamp>/に生成される
gulp test
# レポーターを開く
gulp openReport

backstop.jsonは以下を使った。jsonの他にjavascriptもつかえる様子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
"viewports": [
{
"name": "phone",
"width": 320,
"height": 480
}
],
"scenarios": [
{
"label": "http://getbootstrap.com",
"url": "http://getbootstrap.com",
"hideSelectors": [],
"removeSelectors": [
"#carbonads-container"
],
"selectors": [
"header",
"main",
"footer"
],
"readyEvent": "backstopjs_ready",
"delay": 500,
"misMatchThreshold" : 0.1,
"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
}

レポーターを開くとテスト結果とそれぞれの要素が表示される。

viewportsで複数のviewportのプロパティを指定することができる。
hideSelectorsは指定要素に visibility:hidden を適用する。これは広告など任意のコンテンツが表示される要素を非表示にする。
removeSelectorsは指定要素に display:none を適用する。未実装などの要素をテストしないようにする。
出力ファイルのパスの設定はpathsとなる。

BackstopJSはセットアップからテストの実施までインストールしたbackstopjsのgulpを実行する必要があるので、npm scriptsを作っておくとかで対応した。

1
2
3
4
5
6
7
8
9
{
...
"scripts": {
"backstop-reference": "cd ./node_modules/backstopjs && gulp reference",
"backstop-test": "cd ./node_modules/backstopjs && gulp test",
"backstop-report": "cd ./node_modules/backstopjs && gulp openReport"
},
...
}

複数のviewportに対してテストをまとめて実行できる点は、レスポンシブサイトではすごく重宝しそう。
ツール自体はgulpへの依存度が大きく、すでにワークフローが確立しているプロジェクトなどでは導入の際に検討が必要となりそう。

Succss

Succss も導入の簡単なRegression test toolらしく、こちらは2015年にリリースされているので比較的新しい。

1
2
3
4
5
6
7
npm i -g success
# 現在の状態のscrennshotを追加する
succss add configuration.js
# 変更を比較する
succss check configuration.js

設定ファイル(configuration.js)は以下を使用した。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Succss.pages = {
'homepage': {
url:'succss.ifzenelse.net/',
directory:'screenshots/',
captures: {
'header':
{
'before': function() {
this.click('#logo-image');
console.log('... Waiting for color squares reinitialization');
this.wait(900, function() {
console.log('Done!');
});
}
}
}
}
}
Succss.viewports = {
'default': {
width: 320,
height: 240
},
'mobile-landscape': {
width: 640,
height: 480
},
}

directoryはscrennshotの保存先ディレクトリ。
capturesで比較する要素css selectorsで指定できる。また関数を使うことも可能。

Succssは設定がシンプルかつ複数のviewportに対してテストを実行でき、オプションも豊富である。
画像を比較するdiffツールにResemble.jsのほかimagediff.jsも使うことができる点で他と違っている。

特定のタスクランナーに依存してないので既存のプロジェクトに導入しやすい感じがある。

PhantomCSS

PhantomCSSCasperJSモジュールとして機能するツール。

PhantomCSS自体のinitializeはdemoを参照すると良さそう。

今回はdemoを使って軽く試してみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var phantomcss = require('phantomcss');
casper.test.begin('Coffee machine visual tests', function ( test ) {
phantomcss.init({
casper: casper,
screenshotRoot: 'screenshots',
failedComparisonsRoot: 'failures',
comparisonResultRoot: 'results',
libraryRoot: 'node_modules/phantomcss',
addLabelToFailedImage: false,
});
casper.on( 'remote.message', function ( msg ) {
this.echo( msg );
} )
casper.on( 'error', function ( err ) {
this.die( "PhantomJS has errored: " + err );
} );
casper.on( 'resource.error', function ( err ) {
casper.log( 'Resource load error: ' + err, 'warning' );
} );
casper.start('coffeemachine.html');
casper.viewport( 1024, 768 );
casper.then( function () {
casper.click( '#coffee-machine-button' );
// wait for modal to fade-in
casper.waitForSelector( '#myModal:not([style*="display: none"])',
function success() {
phantomcss.screenshot( '#myModal', 'coffee machine dialog' );
},
function timeout() {
casper.test.fail( 'Should see coffee machine' );
}
);
} );
casper.then( function now_check_the_screenshots() {
phantomcss.compareAll();
} );
casper.run(function () {
console.log( '\nTHE END.' );
casper.test.done();
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 現状のscreenshotを生成
casperjs test test.js
Test file: test.js
# Coffee machine visual tests
New screenshot at results/coffee machine dialog_0.png
Must be your first time?
Some screenshots have been generated in the directory results
This is your 'baseline', check the images manually. If they're wrong, delete the images.
The next time you run these tests, new screenshots will be taken. These screenshots will be compared to the original.
If they are different, PhantomCSS will report a failure.
THE END.
# わざと差分を出して比較テストを行う
asperjs test test.js
Test file: test.js
# Coffee machine visual tests
Failure! Saved to failures/coffee machine dialog_0.fail.png
FAIL Visual change found for screenshot results/coffee machine dialog_0.png (8.65% mismatch)
# type: fail
# file: test.js
# subject: false
PhantomCSS found 1 tests, 1 of them failed.
PhantomCSS has created some images that try to show the difference (in the directory failures). Fuchsia colored pixels indicate a difference betwen the new and old screenshots.
THE END.
FAIL 1 test executed in 4.162s, 0 passed, 1 failed, 0 dubious, 0 skipped.
Details for the 1 failed test:
In test.js
Coffee machine visual tests
fail: Visual change found for screenshot results/coffee machine dialog_0.png (8.65% mismatch)

差分が出た場合は以下のように差分が視覚的に確認できる。

PhantomCSSはドキュメントを見る限りでは、オプションが色々とありCasperJSのテストが書ければなんとかなりそうな雰囲気。
既存のプロジェクトに導入しても十分活躍できそう。
複数の複雑な条件をテストする際はCasperJSのコードを頑張っていく感じになりそう。

まとめ

どれも基本的に PhantomJS + CasperJS でscreenshotを取得して、resemble.jsで画像を比較するパターンとなっている。
(slimerjsもオプションで使える)
Regressionテストを導入する場合、プロジェクトによってどれを選択するのか判断しないといけなが、個人的には以下のように考えた。

既にstyleguideがある場合はcsscriticをstyleguideに導入するのは簡単なので良さそう。
Gulpを使っているプロジェクトで簡単に導入したいならBackstopJSは強力なのでこちらを選択する。
タスクランナーに依存しないで、シンプルに導入したいならSuccssで問題ない。
CasperJsでテストコード書くことが可能な場合は、複雑な条件でテストできるようにしたいならばPhantomCSSを選んでおくのが良さそう。

Comments