この記事は「WACUL Advent Calendar 2017」の20日目です。
WACULでフロントエンドエンジニアをしている@bokuwebと申します。
はじめに
みなさんはお気に入りのテスト構成はあるでしょうか。よく耳にするのはjest
、AVA
、karma
を用いた構成でしょうか。個人的には最近はAVA
を使用することが多いのですが、その際の不満を解消するためにモジュールを作った話しを書きたいと思います。
つくったもの
AVAとは
avajs/ava: Futuristic JavaScript test runner
紹介記事は他にも色々ありますので多くは書きませんが、sindresorhus氏作の、曰く未来型のテストランナーです。若干古いですが以下に日本語での記述もあり、なぜAVAなのか?
に特徴が記載してあります。
例えばPromise
を使用したテストは以下のように記述できます。同様にObservable
もサポートしています。
import test from 'ava';
test('Promise test', async t => {
const result = await somePromise();
t.is(result, 'Hogeeeeee');
});
以前は自分Karme + mocha + power-assert
のような構成が多かったんですが、設定が非常に大変だったんですが、AVA
の場合は設定がシンプルにできるのも良い点です。
また、AVA
が使用しているmagic assert
(これが正式な呼称かは知らないが、PRにはそう書いてあった)は以下のようにテスト失敗時の差分が見やすい点もお気にいりです。
他にもJest
と同様のスナップショットテストを行えるなどの利点もあります。
解決したかった点
上記では良い点を書きましたが、当然不満な点もあります。webpack
でローダーを駆使したプロジェクトとの相性が悪かったり、Typescript
使用の場合一手間必要だったり細かい点はいくつかあるのですが、自分が解決したかったのは以下の2点です。
- 1 本物のブラウザで実行したい
- 2 スクリーンショットを取りたい
上記は独立しておらず、2を解決するには1を解決する必要があるんですが。。。
1 本物のブラウザで実行したい
AVA
はNodeJS
のプロセス上で動作する前提で設計されているのでブラウザ上で実行することができません。現状はbrowser-env
を使用することでのテストを推奨しています。
メンバーも本物のブラウザで動作させることはlow priorityだと言っておりそこまで関心は高くないようです。(長い間開いてたissueもいつの間にか閉じられてた・・・)
正直、ドラッグやファイルアップロードなどbrowser-env
ではまかなえないケースはレアですし、SPAなどのテストではここまで必要となるケースは少ないかもしれません。(こいったコンポーネントは切り出してPuppeteerなどでテストすることもできますし。)
自分の場合は前述したように、2の解決に1が必要であったのと、たまたまそのようなテストを行いたいコンポーネントがあったため、モチベーションのひとつとして記載しています。
2 スクリーンショットを取りたい
どちらかと言えば、こちらがメインのモチベーションです。弊社としても個人的にもVisual Testing
を使用することが多いのでこちらは是非とも解決したい点でした。
Visual Testing
については@Quramyの以下の記事が詳しいです。Avaron
も紹介してもらってます。
1日10万枚の画像を検証するためにやったこと - Qiita
Visual Testing
というと思い浮かべるスコープは人やチームによってまちまちかと思います。E2E
レベルでのページ単位のスクリーンショットを思い浮かべる方も多いと思いますが、個人的にはコンポーネント単位で小さな単位でのスクリーンショット、すなわちjest
のsnapshot test
の画像版のような使用方法がお気に入りです。そのへんの考え方は以下の資料にも記載しています。
煩悩レスなコンポーネントテストを目指して // Speaker Deck
Avaronについて
解決方法
資料にも少し記載はありますが、AVA
がfork
するprocessをElectron
に無理やり差し替えています。(現状かなり力技でイケてないんですが、中の人曰くこの辺りを抽象化してprocess
を差し替えられるようにするアイディア自体はあるようですが、low priorityとなっており、実現は遠そうです。)
Electron
さえ立ち上がれば後はrenderer process
で実行させてやればChromium
1
上でのテストが可能なりますし、Electron
のwin.capturePage
を使用すればスクリーンショットも取れるので解決したかった点は両方解決されます。
使用方法
以下のように設定することでrenderer
でのテストが可能です。また、もともとAVA
を使用していた方は"test": "ava"
となっていた箇所を"test": "avaron"
とすればそのまま使用可能ですし、Avaron
がAVA
本体へ干渉することはないので、併用も可能(なはず)です。
windowOptions
はElectron
のBrowserWindow
へ渡すパラメータです。Windowのサイズなどその他いろいろここから設定可能です。また、fixture
はテストを実行するhtml
を指定することができます。(karmaで言うところのcustomContextFile
です)
{
"scripts": {
"test": "avaron"
},
"devDependencies": {
"avaron": "*"
},
"avaron": {
"renderer": true,
"fixture": "./fixture.html",
"windowOptions": {
"title": "avaron"
}
}
}
例えば以下のようなテストでキャプチャが取得できます。
import test from 'ava';
import React from 'react';
import { render } from 'react-dom';
import { screenshot } from 'avaron';
test('should capture react component screenshot', async t => {
render(<Example />, document.querySelector('.main'));
datePicker.show();
const path = 'screenshots/date-picker-dialog.png';
await screenshot(path);
});
上記は抜粋ですので、全体は以下を参照ください。
注意点
注意点としてはCIなどで動作させる場合、仮想ディスプレイが必要になるので注意してください。具体的にはXvfb
などを設定する必要があります。以下にサンプルを記載します。
- TravisCI https://github.com/bokuweb/react-avaron-sample/blob/master/.travis.yml
- CircleCI(v1) https://github.com/bokuweb/react-avaron-sample/blob/master/circle.yml
- CircleCI(v2) https://github.com/bokuweb/flownes/blob/master/.circleci/config.yml
使用例
Avaron
自身の紹介からは少しはずれますが、@Quramy作のreg-suit
を使用した例を紹介してみたいと思います。
リポジトリは以下で上記のカレンダーの日付を変更した際の例になっています。
この例ではmaterial-ui
のコンポーネントを使用しているのですが、コンポーネントの見た目の変更を検出できています。
また、先日ファミコンエミュレータの創り方 - Hello, World!編 -という記事を書いたんですが、このエミュレータ開発にもAvaron
とreg-suit
を使用しています。テスト対象の解像度が低いので見にくいですが、エミュレータ用のテストROMを実行した際に差分がでた場合の例になります。(エミュレータのテストROMはエミュレータ上で実行されるためテストに落ちた場合の検出が困難で、見た目も含めて差分を検出できるこの仕組は有用でした。)
さいごに
AVAをブラウザ上で実行する試みについて紹介しましたが、如何でしたでしょうか。
まだまだ不安定なところはあると思いますが、もし興味がありましたら、フィードバックやPRをいただけると幸いです。
-
versionはElectronで使用しているものに縛られてしまうという欠点もありますが ↩