ElectronのRendererプロセスのテスト

この記事ではElectronアプリのRendererプロセスのテスト方法について紹介します。

Electronアプリのテスト方法とTravic CIの連携については、[Electron アプリを Travis CI でテストする](http://qiita.com/Linda_pp/items/cb87ce984cfc8ed2fc7a) に詳しく解説されています。この方法はBrowserプロセス・Rendererプロセス共にテストしていますが、テスト結果が、Browserプロセスのテストのみです。この記事はRendererプロセスのテストを行うステップを解説します。

今回のコードは、[jprichardson/electron-mocha](https://github.com/jprichardson/electron-mocha) を参考にしています。このリポジトリは、ElectronのRendererプロセスでテストを実行できる便利ツールです。

今回はテストファイルをコード内で指定しますが、上記の記事にもあるとおり、`BrowserWindow.webContents.executeJavaScript()` を使うと外部から手軽にJSをロードできるかも知れません。

<i class="fa fa-map-signs"></i> 概要と全体の構成
----------------

ディレクトリの構成は次のとおりです。

```
├── package.json
├── test
│   └── electron_test.js
└── test-runner
    ├── index.html
    ├── index.js
    └── main.js

```

テストフレームワークにはMochaとChaiを使い、ipc経由でテスト結果をBrowserプロセスでターミナルに表示します。

`test` ディレクトリには各テストコードを格納します。`test-runner`ディレクトリは、テストを走らせるためのファイルです。`main.js`は、`BrowserWindow`を作成してElectronを表示します。`BrowserWindow`に表示するHTMLは`index.html` にあり、`index.js` はHTML内で`<script>` タグでロードします。


今回のテストではelectron-prebuiltとmochaとchaiを使うので、あらかじめnpmからインストールしましょう。

```
npm install electron-prebuilt mocha chai
```

<i class="fa fa-list"></i> テストの用意
------------

今回は、次の単純なテストを走らせます。このテストは `window.document` オブジェクトを参照しているため、Rendererプロセスでのみテストがパスします。

```js:test/electron_test.js
describe('Electron renderer process', function() {
  describe('document object', function() {
    it('is an Document object', function() {
      expect(window.document).to.be.an.instanceof(Document);
    });
  });
});
```

<i class="fa fa-briefcase"></i> テストランナー
--------------

### test-runner/index.html

HTMLファイルは単純に、`index.js` を読み込むだけです。

```html:test-runner/index.html
<!doctype html>
<html>
  <head>
    <script src="./index.js"></script>
  </head>
  <body></body>
</html>
```

### test-runner/index.js

`index.js` はRendererプロセスで実行されます。このファイルからテストをロードして、テスト結果をipcに送信します。


```js:test-runner/index.js
var remote = require('remote');
var remoteConsole = remote.require('console');
var ipc = require("electron").ipcRenderer;
var mocha = require('mocha');
var expect = require('chai').expect;

Error.stackTraceLimit = Infinity;

console.log = function () {
  remoteConsole.log.apply(remoteConsole, arguments);
};

console.warn = function () {
  remoteConsole.warn.apply(remoteConsole, arguments);
};

console.dir = function () {
  remoteConsole.dir.apply(remoteConsole, arguments);
};

window.onerror = function (message, filename, lineno, colno, err) {
  ipc.send('mocha-error', {
    message: message,
    filename: filename,
    err: err,
    stack: err.stack
  })
};

var runner = new mocha();
runner.reporter('nyan');
runner.ui('bdd');
runner.files = mocha.utils.lookupFiles('test', ['js'], true);
runner.run(function (failureCount) {
  ipc.send('mocha-done', failureCount);
});
```

まず `console.log = function() { ... }` で`console.log` メソッドを再定義して、`remote.require('console')` に流します。これでRendererプロセスのconsole出力がBrowserプロセスのconsoleへ渡されるので、テストの結果をターミナルに流すことができます。

例外発生などのエラーは、`window.onerror` にイベントリスナを設定し、`mocha-error` イベントをipcに渡してRendererプロセスに通知します。Mocha終了時は `mocha-done` イベントをipcに渡して、FAIL数をRendererプロセスに通知します。


### test-runner/main.js

Browserプロセス側の、BrowserWindowを作成する`main.js`です。

```js:test-runner/main.js
var app = require('app');
var ipc = require('electron').ipcMain;
var BrowserWindow = require('browser-window');
var path = require('path');
var util = require('util');

process.on('uncaughtException', function (err) {
  console.error(err);
  console.error(err.stack);
  process.exit(1);
});

app.on('ready', function () {
  var win = new BrowserWindow({ height: 700, width: 1200 });
  win.loadURL('file://' + __dirname + '/index.html');
  ipc.on('mocha-done', function (event, failureCount) {
    process.exit(failureCount > 0 ? 1 : 0);
  }); 
  ipc.on('mocha-error', function (event, data) {
    process.stderr.write(util.format('\nError encountered in %s: %s\n%s',
      path.relative(process.cwd(), data.filename),
      data.message,
      data.stack));
    process.exit(1);
  }); 
});
```

Rendererプロセスで送信するメッセージ `mocha-done` と `mocha-error` を受け取ります。`mocha-done` はMocha終了時に送信され、`failureCount` が0より大きいと終了コード1で終了するため、CIなどではテストがFAILするようになります。


<i class="fa fa-check-square-o"></i> テスト実行
----------

テスト実行は、`electron` に `main.js` を指定します。

```
./node_modules/.bin/electron test-runner/main.js
```

テストがPASSすると、かわいいnyancatが画面を走ります。

![success.png](https://qiita-image-store.s3.amazonaws.com/0/102667/2806d771-c650-1796-5eb3-2c1a28c43c88.png)


またテストがFAILすると、通常のMocha同様、FAILしたテストが表示されます。

![fail.png](https://qiita-image-store.s3.amazonaws.com/0/102667/0a560466-ae92-173b-10e5-53ea7dfc10cb.png)


Travic CIとの連携については、[Electron アプリを Travis CI でテストする](http://qiita.com/Linda_pp/items/cb87ce984cfc8ed2fc7a) に詳しく解説されているので、参照してみてください。