自分の開発環境では, nodeで単体テストと分離してモデル層を抽出出来たが、e2eほどではないがブラウザ上でテストしたいコードというのは結構ある。モーダル制御とか、ブラウザ上のイベントに依存する奴とか。
それらを Nightmare でテストするアプローチを紹介する。
概要
Nightmare はヘッドレステストランナーとそのDSLを提供する。v2でランナーがphontomjsから Electronになった。
コードはbabel/commonjsで書かれており、本番環境でJSは1つにまとまっているが、Electron の nodeIntegration を有効化して走らせることで、ビルド前のコードを個別にrequireできる。グローバルを避けてrequireで依存が明示されていれば、理論上そのコードは完結して動くはず。
実装例
適当なエントリー用のHTMLを用意する。必要だったらここで各種コンテナを用意してもいい。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<h1>Blowser Test Env</h1>
</body>
</html>
ヘルパを用意する。
'use strict';
// nightmare
const Nightmare = require('nightmare');
const path = require('path');
const TEST_HTML_PATH = "file://" + path.join(__dirname, "test.html");
// create
global.browser = null;
let startBrowser = function * (){
global.browser = Nightmare({
show: false,
nodeIntegration: true
});
yield browser
.goto(TEST_HTML_PATH)
.wait('body')
.evaluate(() => {
require("babel-register"); // to evaluate spec-helper
require("../src/main"); // 初期化コードを読み込む
});
}
let finishBrowser = function * (){
yield global.browser.end();
}
exports.useBrowser = function() {
// should init in mocha env
require("mocha-generators").install();
before(function * () {
yield startBrowser();
});
after(function * () {
yield finishBrowser();
});
}
'use strict';
var helper = require('./browser-spec-helper');
describe('browser specs', () => {
helper.useBrowser();
it('render foo view', function*() {
var qiitaProps = yield browser
.evaluate(function () {
// Backboneっぽい擬似コード
let FooView = require("../src/foo-view");
new FooView().renderTo("body");
});
yield browser.wait('.foo-inner');
});
});
mocha で実行。
$ mocha -r browser-spec/spec-helper browser-spec/*.js -t 24000
解説
NightmareをnodeIntegration: true
で初期化することでrequireが使用可能になる。
browser.evalueteの中のコードはelectronの中で実行される。コードの式としての評価は実行ホスト側で行われるが、実際に実行されるのはブラウザの中である点に注意。そしてその中はnodeとブラウザプロセスをattachしたelectronのプロセス内で、requireが使える、という仕組み。
mochaと一緒に使う注意点
Nightmareはインターフェースが yield で統一されているのだが、babelのgenerator-transformerは generatorをAST変換して継続渡しスタイルに変換する。これが邪魔なので切っておかないといけない。
上記のコードのテスト部分node v4.2で評価して、babel は使っていない。register('babel-register')を走らせた後のrequire先は babel でも構わない。
まとめ
e2e, nodeのユニットテストという軸だけではやりづらかった、ブラウザでのコンポーネントのユニットテストがこれによって非常にやりやすくなった。
Electronはアプリ配布用としてだけではなく、開発環境をエンハンスするのにもとても便利。