Jestとは
JestはFacebook製のJSテストプラットフォームです。
近頃、急速にユーザーが増えているようで、ある調査では2017年にMochaやJasmineを超えてトップの使用率を獲得しているようです。
出展: https://ashleynolan.co.uk/blog/frontend-tooling-survey-2018-results最近、自分のプロジェクトでもJasmine + KarmaからJestへ移行し、以下のようなメリットを得られました。
- ブラウザの起動がないぶん軽快に動く
- 実ブラウザ上ではなく、JSDOMのエミュレーション環境でテストが実行される
- レポートが見やすい
- テスト実行進捗がプログレスバーで表示され、Failしたテストの閲覧もしやすい
- カバレッジを簡単に取得できる
- 実行時にオプションをひとつ追加するだけ
この記事では、導入時にハマりどころがいくつかあったので、導入方法とともにハマったポイントと対処法を紹介します。
前提
以下バージョンを前提としています。
- Jest 23.1.0
インストール
まず基本となる2つのパッケージをインストールします。
$ npm install --save-dev jest babel-jest
※ Babelを使っていない場合には babel-jest
は不要ですが、多くのプロジェクトで使われていると思われるため、以下の説明ではBabelを使用していることを前提に書いています
TypeScriptファイルをテストしたい場合は、 加えて ts-jest
をインストールしてください。
$ npm install --save-dev ts-jest
Vueファイルをテストしたい場合には、 加えて vue-jest
をインストールしてください。
$ npm install --save-dev vue-jest
設定ファイル
jestの設定を定義します。
設定を定義する場所は以下の選択肢があります。
- 独立したファイルに定義する
-
package.json
に定義する
それぞれ、設定できる内容に違いはありませんので、どちらを選択しても問題ありません。
1. 独立したファイルに定義する場合の例
module.exports = {
verbose: true
};
2. package.jsonに定義する場合の例
{
"name": "my-project",
"jest": { // <-- jestプロパティ以下に設定を書く
"verbose": true
}
}
設定例
テストを始めるにあたって必要な設定を行います。
jest.config.js
ファイルをプロジェクト直下に作成、もしくは、package.json
ファイルに追記します。
module.exports = {
transform: {
'^.+\\.js$' : '<rootDir>/node_modules/babel-jest',
'.*\\.(ts)$' : '<rootDir>/node_modules/ts-jest', // TypeScriptファイルをテストする場合
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest' // Vueファイルをテストする場合
},
moduleFileExtensions: ['js', 'ts', 'vue'] // テスト対象の拡張子を列挙する
}
上記のように定義することで、トランスパイルしたうえでテストを実行してくれるようになります。
このとき、 babel-jest
は .babelrc
、 ts-jest
は .tsconfig.json
を参照しトランスパイルを行います。
import/export構文を使えない問題の対処
多くのブラウザアプリケーションではESModuleの import/export
構文を使用しているかと思います。
しかし、JestはNode.js上でテストを実行するため、Node.jsのモジュールシステムのCommonJSである必要があります。
(現時点のNode.js <= v10ではフラグを立て、ファイル拡張子の変更を行わないとESModuleに対応させることができません)
たとえば、下のような .babelrc
の場合、ESModuleはCommonJSに変換されないため、このままJestを実行するとエラーとなります。
{
"presets": [
["env", {
"modules": false // <-- CommonJSに変換していない
}]
]
}
そこで、テスト実行時のみ ESModuleをCommonJSに変換することで、Jestでもimport/export
構文を使用できるように対応します。
まず、babelのpluginをインストールします。
$ npm install --save-dev babel-plugin-transform-es2015-modules-commonjs
test環境でのみpluginを有効とするように .babelrc
に以下のように設定を追加します。
{
"env": {
"test": { // <-- NODE_ENV=testの場合のみpluginが有効になる
"plugins": [
"transform-es2015-modules-commonjs"
]
}
}
}
参考: https://github.com/facebook/jest/issues/2081
テストファイル
以下のsum.js
のテストを書いてみます。
function sum(a, b) {
return a + b;
}
export default sum;
Jestは*.test.js
もしくは*.spec.js
をテストファイルとして検出しテストを実行します。
また、__tests__
ディレクトリ以下のすべてのファイルをテストファイルとして扱います。
ここではファイル名をsum.test.js
としました。
import sum from './sum';
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
テストの記法については他に詳しく解説してくれている記事があるので、ここでは説明を省きます。
自分は下の記事を参考にしました。
Jasmineやmochaなど、別のJSテストフレームワークを使った経験がある方なら、Jestでもほとんど同じ書き方ができるため、すぐに書き始められると思います。
テストの実行
npm-scriptsに、Jestを実行するコマンドを設定します。
"scripts": {
"test": "NODE_ENV=test jest"
},
コマンドラインより実行
$ npm test
以下のようなレポートの出力を確認できたら成功です
カバレッジの取得
カバレッジの取得は、--coverage
オプションを加えるだけで実現できます。
"scripts": {
"test": "NODE_ENV=test jest ./path/to/test_directory",
"test-with-coverage": "npm test -- --coverage" // <-- カバレッジも取得する場合はこちらを実行
},
コマンドラインより実行
$ npm run test-with-coverage
カバレッジのレポートが表示されます。
また、coverage/lcov-report/index.html
を開くと詳細なレポートを参照できます。
ESLintの対応
ESLintを使っている場合、テストファイル中のtest
やexpect
などの記述がno-undef
としてエラーとなる場合があります。
これは、ESLintのJestプラグインを導入することで解決できます。
$ npm install --save-dev eslint-plugin-jest
.eslintrc
に以下の設定を追加します。
{
"env": {
"jest/globals": true
},
"plugins": [
"jest"
],
}
テストファイルのマイグレーション
他のテストフレームワークからJestへ移行する場合、マイグレーションガイドを参考にすると良いでしょう。
ガイドではjest-codemodsというツールが紹介されています。
このツールを使うとテスト記法を自動でJestに対応するように変換してくれるようです。
Jasmineからの移行の場合、ほぼ互換性があるようで、自分のプロジェクトでは既存のテストファイルに何も手をいれずともJestで動いてくれました。
JSDOMに不足しているAPIをmockで補う
JestはNode.js上でブラウザ環境をエミュレートするために、JSDOMという実装を使っています。
このJSDOMはブラウザ環境のAPIを提供してくれているのですが、いくつか実装されていないAPIも存在するため、それらについてはmockで補う必要があります。
自分はテストを書くなかで、以下の不足しているAPIを見つけました。
- Web Storage API(local storage, session storage)
- window.getSelection
Web Storage APIはnpm packageでmockをインストールできます。
$ npm install --save-dev mock-local-storage
そして、インストールしたmockをimportするファイルを作成します。
さらに、window.getSelection
のmockもこのファイルに記述します。
import 'mock-local-storage';
// https://github.com/jsdom/jsdom/issues/937
window.getSelection = () => {
return {
addRange: () => {},
removeAllRanges: () => {}
};
};
作成したファイルをテスト起動時に読み込むように、以下の設定をjest.config.js
(もしくはpackage.json)に追加します。
module.exports = {
setupTestFrameworkScriptFile: '<rootDir>/jest-setup.js' // <- 追加
}
このようにすることで、テスト起動時にmockを読み込ませ、エラーとなることを防ぐことができます。
おわりに
この記事で紹介した設定等はGithubに上げているので、よかったらこちらも見てやってください!
https://github.com/hogesuke/jest-demo