.babelrcを使わずに独自のBabelの設定を使ってJestを実行する

この記事は.babelrcの設定を使わずにJest用の独自のBabelのオプションを設定する方法を紹介する記事です。

Node.jsで書かれたサーバーサイド用のテストとReactで書かれたフロントエンド用のテストでBabelの設定を変更したいために調べた内容です。他にも方法があれば教えていただけると助かります。

今回の記事ではReactのテストを例にしていますが、他のフレームワークやライブラリにも適応できます。

# JestでBabelを使う
まずはじめにJestは設定をJavaScriptで書くことができます。
ファイル名を`jest.config.js`にしておくか、Jestを実行するときに`--config <path/to/file>`のように`--config`オプションをつけて設定ファイルを指定することができます。
JestでBabelを使ってテストを実行するためには[babel-jest](https://www.npmjs.com/package/babel-jest)というパッケージが必要です。
以下はReactのテストを実行するための最小の設定です。コードは`client`というディレクトリにある想定です。
ここで注目すべきは`"transform"`です。ここでbabel-jestを指定することでJestの実行時にBabelを使います。


今回はReact用にJestを設定する例をとるため、`jest.config.react.js`という名前にします。(名前は任意のため何でも良い)

```js:jest.config.react.js
module.exports = {
    "testMatch": [
        "<rootDir>/client/__tests__/**/*.test.js",
    ],
    "moduleFileExtensions": [
        "js",
        "jsx",
    ],
    "moduleDirectories": [
        "node_modules",
        "client",
    ],
    "transform": {
      "^.+\\.jsx?$": "babel-jest",
    }
};

```


# babel-jestにBabelの設定を指定する
babel-jestですが、デフォルトではプロジェクト内の.babelrcに書かれたBabelの設定を使います。
しかし、前述のとおりフロントエンドとサーバーサイドで別のBabelの設定で実行したいため、できれば.babelrcをいじりたくはありません。

そういった時のためにbabel-jestには`createTransformer`というAPIが用意されています。このAPIを使うことでJestの実行時に使うBabelの設定を指定することができます。

まず、Babelの設定ファイルを用意します。`babelrc`を`false`にすることを忘れないようにしてください。

```js:custom-transformer.js
const babelJest = require("babel-jest");
const babelOptions = {
    presets: [
        ["@babel/env", {
            "targets": {
                "node": "current",
            },
            "exclude": ["transform-regenerator"],
        }],
        "@babel/react",
    ],
    babelrc: false,
};
module.exports = babelJest.createTransformer(babelOptions);

```

次に前述で出てきたJestの設定ファイル内の`"transorm"`の値を上記のBabelの設定ファイルのパスに変更します。

```diff:jset.config.react.js
module.exports = {
    "testMatch": [
        "<rootDir>/client/__tests__/**/*.test.js",
    ],
    "moduleFileExtensions": [
        "js",
        "jsx",
    ],
    "moduleDirectories": [
        "node_modules",
        "client",
    ],
    "transform": {
-     "^.+\\.jsx?$": "babel-jest",
+     "^.+\\.jsx?$": "path/to/custom-transformer.js",
    }
};
```

ここで上記の`custom-transformer.js`を作らずに以下のように直接Babelの設定オブジェクトを`"transorm"`の値に設定すればいいのではないかと思うかもしれませんが、`TypeError: customJSTransformer.includes is not a function`とエラーになります。

```js
module.exports = {
    "testMatch": [
        "<rootDir>/client/__tests__/**/*.test.js",
    ],
    "moduleFileExtensions": [
        "js",
        "jsx",
    ],
    "moduleDirectories": [
        "node_modules",
        "client",
    ],
    "transform": {
        "^.+\\.jsx?$": {
            presets: [
                ["@babel/env", {
                    "targets": {
                        "node": "current",
                    },
                    "exclude": ["transform-regenerator"],
                }],
                "@babel/react",
            ],
            babelrc: false,
        }
    }
};
```

Issueもありますが、上記のようにBabelの設定ファイルを別で分けておくしか解決方法はなさそうです。

[Jest-babel createTransformer Error: customJSTransformer.includes is not a function · Issue #7015 · facebook/jest · GitHub](https://github.com/facebook/jest/issues/7015)

自分は最初`"transform"`に直接Babelの設定を書いててエラーになりました。もしかするとこのIssueが閉じられるころには修正されているかもしれませんが、現時点(2019/03/11時点)ではこのエラーは発生します。そのため、Babelの設定は分けてください。

# Jestを実行
JestとBabelの設定はできたので`jest --config jest.config.react.js`を実行すると、`custom-transform.js`にて設定したBabelの設定を使ってJestを実行することができます。

# 参考
* [Testing React Apps · Jest](https://jestjs.io/docs/en/tutorial-react.html#custom-transformers)
* [Using jest with custom babel config · Issue #6368 · facebook/jest · GitHub](https://github.com/facebook/jest/issues/6368)

最後までお読みいただきありがとうございました。不備や質問はコメント欄または[Twitter](https://twitter.com/shisama_)にメンションしてもらえると助かります。