特殊なパターンでavaのテストがこけてしまうので、avaのbabelを切りたかったが結構苦労したので備忘録。
テストが失敗するパターン
呼び出す関数が通常の関数なのか、AsyncFunctionなのかによって分岐する下記のようなプログラムで問題が起きた。
async/awaitはNode.js v7からかなり高速化しているため、テストの高速化も期待してNode.js v7で開発をしている。
expressのrouteに登録する関数の共通処理です。
module.exports = (controller) => {
if (controller.constructor.name === 'AsyncFunction') {
return (req, res) => {
controller(req, res)
.then((data) => {
res.status(200).json(data);
})
.catch((err) => {
res.status(500).send('error');
});
};
}
return (req, res) => {
try {
const data = controller(req, res);
res.status(200).json(data);
} catch (err) {
res.status(500).send('error');
}
};
};
失敗するテストコードは以下
'use strict';
const { test } = require('ava');
const express = require('express');
const request = require('supertest');
const controller = require('./controller.js');
test('async functionを呼び出せる', async (t) => {
const responseObj = { hoge: 'hogehoge' };
let counter = 0;
const func = async () => {
const get = (ms) => {
return new Promise((resolve) => {
counter++;
resolve(responseObj);
});
};
return await get(100);
};
const app = express();
app.get('/', controller(func));
const res = await request(app)
.get('/')
.set('Accept', 'application/json');
t.is(counter, 1, 'async functionが呼び出されている');
t.is(res.status, 200, '正常responseを返せている');
t.deepEqual(res.body, responseObj, '返却するreponseの形式が正しい'); // ここがこける
});
原因
デバッグしてみると通常ならば以下のif文に入ってAsyncFunctionとして処理してくれるはずなのだが、controller.constructor.name
がFunction
になってしまい処理がおかしくなっていた。
if (controller.constructor.name === 'AsyncFunction') {
}
avaが内部的にbabelを利用しているため通常関数にコンパイルされてしまっていた。
調査
要はasync/awaitをコンパイルしないようにしたいので、babelを切れるオプションを探したら下記のissueにあたった。
Disable babel via --no-babel
flag · Issue #424 · avajs/ava
--no-babel
とかいうドンピシャなオプションがあるじゃないかということで早速実行したらエラー
> ava -v --no-babe
✖ Unexpected Babel configuration for AVA. See https://github.com/avajs/ava#es2015-support for allowed va
lues.
下記のissueを見るとこのコマンドを実行すると"babel": false
になるらしい。
https://github.com/avajs/ava/issues/448#issuecomment-190286370
エラー文が何を示してるのか探ってみたらavaのconfigにfalseが入ると問答無用でErrorを飛ばされていた。
https://github.com/avajs/ava/blob/90fb60a895bff0a76df46e2af8deec307a67cbbb/lib/babel-config.js#L19-L22
つまりオプションはあるけど実質指定できない状態のようだ。
conf === 'inherit'
はどこで指定するんだと思ったらREADMEに書いてあった。
https://github.com/avajs/ava#configuration
"babel": false
を設定したら同じエラーになったので、どうやらここの様子。
対処
babelそのものをoffにするのは難しそうなので、babelで使っているpresetsを強制的に空にすればいいじゃないか、という結論に達した。
下記のドキュメントに沿ってavaの中で使っているpresetsを空で上書きをしたら無事async/awaitがコンパイルされずテストが通った。
https://github.com/avajs/ava#es2017-support
...
"ava": {
"babel": {
"presets": []
}
}