2019-03-21

QUnit で CI したい その 1

2 月に転職した。

へーしゃでの初めての仕事はフロントエンド、と言ってもトラッキングに関わる部分である。

生の ES5 で書かれててGoogle Closure Compilerで minify されてるようなのだけど、困ったことにユニットテストがない様子? どこそこのチームで作ってたことがあったらしいという情報は得たので、それを CI に組み込めたらいいかなと思っていた。

蓋を開けてみると、2 年間ほどメンテされていない死んだユニットテストで、最新のコードベースに対するテストでなかった(テストコードと同ディレクトリにテスト対象のコードがコピられてて)🤔

CI に組み込まれないテストは陳腐化すると云うが、まさかそれを身をもって実感することになろうとは思わなんだ。

仕方ないので既存の資産を活かすためにも、テスティングフレームワークなど既存のものを利用して少なくとも CI に組み込めるところまでは持っていこうと考えた。CI に組み込まれればビルド失敗の通知を恐れてみんなユニットテストを書くのをサボらなくなる。

というわけで、許可を取り付けた上で仕事の合間を縫ってユニットテストの整備を行うことにした。

現状把握と展望

ユニットテストはQUnitで書かれていて、モッキングフレームワークにはSinon.JSが使われていた。

linter はESLint - Pluggable JavaScript linterを用意してあるようなのだけどどうも長らく利用されてなかったようで、試しに実行すると 41 件の autofix 可能なエラーが...😅

ブラウザでindex.htmlを開くことでテストが実行されるタイプのやつで、CLI での実行は用意されてなかった。 プロダクトコードがwindowオブジェクトに依存してることもあって、QUnit のテストをヘッドレスブラウザで実行するのが良さげかな。

ここにカバレッジ計測も追加して、最終的にはプルリクをトリガーにした自動テストが CI に組み込まれるのが良さげかなと思う。

やったこと&やらなかったこと

とりあえずカバレッジは置いといて今ある QUnit の CLI 実行を優先した。

node でのカバレッジ計測に関しては知らないことが多かったので軽く下調べだけした。 istabul というやつが node 界隈で強かったみたいだが、こいつは 2 年ほどメンテされてなくて、後続のnyc - npmと言うやつがみつかる 👀 puppeteer もそれ単体でカバレッジが測れるようす ↓

やったこと

まず元々ある QUnit もそのまま使えないとブラウザで見つつの開発の利便性も下がっちゃうので、そこは担保したかった。 (windowオブジェクトがないので CommonJS は死ぬ)

qunit-puppeteer - npmは簡単にテスト実行できて楽だったのだけど、テストの url を絶対パスで指定しないといけず&自分で URL をこねくり回せなくて却下。

なのでそのへん柔軟なnode-qunit-puppeteer - npmしか選択肢が残らなかったわけだ。

とりまeslintだけあるからソイツはそのままに、QUnit を headless browser 実行するための module だけ足す。

npm install --save-dev node-qunit-puppeteer

package.jsonは以下のような感じにした

  "scripts": {
"lint": "eslint --fix ./",
"test": "node test/run.js"
},

lintはメンテされてなかった関係で 40 件くらいの error があるけど、最終的には実行できるようになる(ハズな)ので用意しておく。

test/run.jsは以下の通りで、ameshkov/node-qunit-puppeteer: A simple node module for running qunit tests with headless Chromiumの Example とほぼ同じで行ける便利さ。しびれる。

#! /usr/bin/env node
// almost the same as the sample code :-p
// https://github.com/ameshkov/node-qunit-puppeteer

const path = require("path");
const { runQunitPuppeteer, printOutput } = require("node-qunit-puppeteer");

const qunitArgs = {
targetUrl: `file://${path.join(__dirname, "/index.html")}`,
redirectConsole: true,
};

runQunitPuppeteer(qunitArgs)
.then((result) => {
// Print the test result to the output
printOutput(result, console);
if (result.stats.failed > 0) {
// Handle the failed test run
// currently notghing to do.
}
})
.catch((ex) => {
console.error(ex);
});

実行結果は以下のような感じで出る。

$ npm run test

> qunit@1.0.0 test C:\Users\takatoshi\dev\javascript\qunit
> node test/run.js

Module: hello module
hello test
Status: success
Passed assertions: 2 of 2
Elapsed: 1ms

Test run result: success
Total tests: 1
Assertions: 2
Passed assertions: 2
Failed assertions: 0
Elapsed: 8ms

と、ここまで書いた内容を会社のコードに組み込んだのだけど、会社で作ったものは外に出せないので模倣したゴミプロジェクトを作った ↓

krymtkts/qunit-trial: sandbox to enhance legacy QUnit test.

今後いじくり回すときの砂場としても使おうかな。

あとやりたいこと

ユニットテストだけあってカバレッジ計測がないのはちょっとアレなので早々に追加したい所存 🤔

そもそもユニットテスト書くにしてもどの経路通ったとかわからないのでテスト品質を保証しづらく、コード網羅率 Level C1 を 100%にしたいマンなのもありカバレッジ必須。

でも事前に調べてた nyc や puppeteer での方法だと、現状の node-qunit-puppeteer を使ったテストのカバレッジ計測できなさそう...

もう少し調べる必要がありそう 😳

続く