概要
- 「Next.js」+「TypeScript」でのフロントエンド開発環境に向けて、Jestを使った単体テスをの設定を記載します。
- 特に、DOMコンポーネントのテスト向けに、「React Testing Library」を使った設定を記載します。
- 同様の設定に関する有用な記事は数多く、二番煎じどころではないが、バージョンや使うライブラリの組み合わせによってハマるポイントも多そうだったので、実際に直面したエラーとその解決策も載せておきます。
前提
- 環境
- next.js: v.12.1.X
- typescript: v.4.7.X
- jest: v.28.1.X
テスト対象となるコードを用意する
以下のコードをサンプルとして用意してみた。
// src/components/Sample.tsx
import emailRegex from 'email-regex';
export const EmailForm = () => {
const form = useForm({
initialValues: { email: '' },
validate: { email: (value) => emailRegex({exact: true}).test(value)? null : 'failed.' }
});
return (
<form onSubmit={form.onSubmit((values) => console.log(values)}>
<TextInput {...form.getInputProps('email')}/>
<Button type="submit">
Push
</Button>
</form>
)
}
また、テストコードは以下のようなものを用意
// src/components/Sample.spec.tsx
import { render, screen } from "@testing-library/react";
import { EmailForm } from "./specs/Sample";
it('changes the class when hovered', () => {
render(<EmailForm />);
screen.debug();
});
レンダリングしたHTMLを確認するだけのテスト。実際には想定しているタグが含まれているか?などをチェックすることになると思います。
基本編
パッケージインストール
こちらの記事を参考に、必要なパッケージのインストールと、設定ファイルの用意をします。
同時に、「React Testing Library」のパッケージもインストールしておきます。
yarn add --dev jest @types/jest ts-jest @testing-library/react
config設定
Jestのコンフィグを設定します。
jest.config.js
ファイルを作成し、以下の設定を記述します。
// jest.config.js
module.exports = {
preset: 'ts-jest',
globals: {
'ts-jest': {
tsconfig: './tsconfig.test.json'
}
}
}
すでに存在している(はずの)tsconfig.json
に代わって、テスト用のtsconfig.test.json
を読み込みます。
tsconfig.test.json
は以下の通り。
// tsconfig.test.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"jsx": "react-jsx"
}
}
ここで大事なポイントは、デフォルトのtsconfig.json
のcompilerOptions
パラメータを上書きすることです。具体的には、"fsx": "preserve"
となっている部分を"jsx": "react-jsx"
に置き換えます。
ドキュメントによると、
-
preserve
は、Babel等によるトランスパイル処理が行われることを前提に、出力の一部が、JSXフォーマットになる方式。 -
react-jsx
は、React v.17から導入された変換方式に従って、JSXフォーマットをJSで解釈可能なフォーマットに変換する方式。
この設定を入れないと、テスト実行環境で、JSXを解釈できずに、下記のようなエラーが出るはず。
SyntaxError: Unexpected token '<'
ここで yarn jest
が通れば設定はおしまい
SyntaxError: Unexpected token 'export'
が発生した場合
こちらの記事によると、ts-jestはESModuleに対応していないとのこと。
そして、導入したライブラリの中にESModuleで提供されているパッケージがあると、そのままではJest上でimportできないようです。
そのため、このようなエラーが出たときは、babel-jest
を使った変換を指示してあげる必要があります。
パッケージインストール
babelを使った変換に必要なパッケージをインストールします。
yarn add --dev babel-jest @babel/preset-env @babel/plugin-transform-modules-commonjs
config設定
babelのコンフィグを設定します。
babel.config.js
ファイルを作成し、以下の設定を記述します。
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
}
]
],
plugins: [
'@babel/plugin-transform-modules-commonjs',
]
}
Jestのコンフィグを修正します。
jest.config.js
を下記のように修正します。
// jest.config.js
module.exports = {
transform: {
'\\.jsx?$': 'babel-jest',
'\\.tsx?$': 'ts-jest'
},
transformIgnorePatterns: [
'/node_modules/(?!email-regex)'
],
globals: {
'ts-jest': {
tsconfig: './tsconfig.test.json'
}
}
}
ここで大事なポイントは、transformIgnorePatterns
として、エラーが発生しているESModuleライブラリを変換対象に加えている点です。
否定先読み表現 (?!hoge)
を使って、該当する名前に合致するライブラリ 以外 を変換対象外としています。
今回は、email-regexが該当しているため、上記のような書き方をしています。ここは環境に合わせて変更してください。
この設定を入れないと、ESModuleを変換してくれないため、以下のエラーが発生し、正しくimportできないことがわかる。
SyntaxError: Unexpected token 'export'
The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string. Consider using the "jsdom" test environment.
が発生した場合
テスト環境の指定の修正が必要になります。デフォルトのままだとNode.js
となっていますが、これを jest-environment-jsdom
に変更します。
パッケージインストール
jest-environment-jsdom
をインストールします。
yarn add --dev jest-environment-jsdom
config設定
Jestのコンフィグを修正します。
jest.config.js
を下記のように修正します。
// jest.config.js
module.exports = {
transform: {
'\\.jsx?$': 'babel-jest',
'\\.tsx?$': 'ts-jest'
},
transformIgnorePatterns: [
'/node_modules/(?!email-regex)'
],
testEnvironment: 'jsdom',
globals: {
'ts-jest': {
tsconfig: './tsconfig.test.json'
}
}
}
babelにより、Next.jsのSWCが無効化されて困った場合
babel.config.json
を利用した際、Next.jsのSWCが無効化され、トランスパイルが思った通りに動かないことがあります。
トランスパイルが走った際に、以下のように、TypeScriptが正しく解釈できなかったことによるエラーが出ます。
info - Disabled SWC as replacement for Babel because of custom Babel configuration "babel.config.js" https://nextjs.org/docs/messages/swc-disabled
info - Using external babel configuration from /workspace/babel.config.js
wait - compiling...
error - ./src/component/index.tsx:15:33
Syntax error: Unexpected token, expected ","
...
このような場合、
-
babel.config.json
のファイル名変更- このままだと、自動的に読み込まれてbabelを利用したトランスパイル処理が走ってしまう。そこで、名前を、
babel.config.my.json
などに変えてあげる
- このままだと、自動的に読み込まれてbabelを利用したトランスパイル処理が走ってしまう。そこで、名前を、
-
jest.config.json
のtransformオプションを追加- jest用のconfigの中で、
configFile
を追加 -
configFile
には、上記名前変更したbabel用configファイルを指定
- jest用のconfigの中で、
// jest.config.js
module.exports = {
transform: {
'\\.jsx?$': ['babel-jest', { 'configFile': './babel.config.my.js' }],
'\\.tsx?$': 'ts-jest'
},
transformIgnorePatterns: [
'/node_modules/(?!email-regex)'
],
testEnvironment: 'jsdom',
globals: {
'ts-jest': {
tsconfig: './tsconfig.test.json'
}
}
}
これで動く(はず)
おしまい
あとはyarn jest
を実行すればテストが動きました。
# yarn jest
yarn run v1.22.19
$ /workspace/node_modules/.bin/jest
PASS src/components/Sample.spec.tsx (37.822 s)
✓ changes the class when hovered (184 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 42.163 s
Ran all test suites.
Done in 48.51s.