0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

avaでテスト時にbabelをoffにする

Last updated at Posted at 2017-05-07

特殊なパターンでavaのテストがこけてしまうので、avaのbabelを切りたかったが結構苦労したので備忘録。

テストが失敗するパターン

呼び出す関数が通常の関数なのか、AsyncFunctionなのかによって分岐する下記のようなプログラムで問題が起きた。
async/awaitはNode.js v7からかなり高速化しているため、テストの高速化も期待してNode.js v7で開発をしている。

expressのrouteに登録する関数の共通処理です。

controller.js
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');
    }
  };
};

失敗するテストコードは以下

test.js
'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.nameFunctionになってしまい処理がおかしくなっていた。

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

package.json
...
  "ava": {
    "babel": {
      "presets": []
    }
  }
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?