Nightmare v2(Electron) でブラウザ上でES2015のコードを個別にrequireしてユニットテストを書く

More than 3 years have passed since last update.

自分の開発環境では, nodeで単体テストと分離してモデル層を抽出出来たが、e2eほどではないがブラウザ上でテストしたいコードというのは結構ある。モーダル制御とか、ブラウザ上のイベントに依存する奴とか。

それらを Nightmare でテストするアプローチを紹介する。

https://github.com/segmentio/nightmare


概要

Nightmare はヘッドレステストランナーとそのDSLを提供する。v2でランナーがphontomjsから Electronになった。

コードはbabel/commonjsで書かれており、本番環境でJSは1つにまとまっているが、Electron の nodeIntegration を有効化して走らせることで、ビルド前のコードを個別にrequireできる。グローバルを避けてrequireで依存が明示されていれば、理論上そのコードは完結して動くはず。


実装例

適当なエントリー用のHTMLを用意する。必要だったらここで各種コンテナを用意してもいい。


browser-spec/test.html

<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<h1>Blowser Test Env</h1>
</body>
</html>

ヘルパを用意する。


browser-spec/spec-helper.js

'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();
});
}



browser-spec/foo-spec.js

'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はアプリ配布用としてだけではなく、開発環境をエンハンスするのにもとても便利。