フロンエンドエンジニアごーです。
vue-test-utilsのドキュメントを読んでいて、Jestというテストフレームワークが紹介されていたので、どんな機能があるのか試してみました。
Jestとは
Facebook社製の快適なJavascriptのテストを実現するためのフレームワーク。
Reactアプリケーションを含む全ての JavaScriptコードのテストに利用可能で、複雑な設定ナシで高速に動作する。
- githubのstar: 13,840 (2017/12/04の時点で)
- 2014年から開発が行われている
単一ファイルコンポーネント(SFC)をテストする
- 事前コンパイルが必要
- Jestプリプロセッサを使用するので、テスト過程でwebpackは不要
- 基本的なSFC(StatelessFunctionalComponent)機能をサポートしているがvue-loaderでサポートされている、スタイルブロックやカスタムブロックは評価していない
スナップショットテスト
初回テストでUIコンポーネントをレンダリングし、結果を.snapshot
というファイルが生成される。
ファイル名は、テストファイルに対応する。
初回移行は、生成されたコンポーネントツリーを以前と比較してUI が予期せず変更されていないかをテストする。
// Jest Snapshot v1, https://goo.gl/fbAQLP exports[`List.vue matches snapshot 1`] = ` <ul> <li> item 1 </li> <li> item 2 </li> </ul> `;
.snapshot
ファイルの扱いに関しては、Jestの公式ドキュメントに記述があるので参考にすると良い。
セットアップ
vue-test-utilのドキュメントにある通りに、以下のサンプルリポジトリをクローンする。
$ git clone https://github.com/vuejs/vue-test-utils-jest-example.git
クローンしたディレクトリに移動して、依存するライブラリをインストール。
$ cd vue-test-utils-jest-example/ $ npm i
vue-jest
単一ファイルコンポーネント(SFC)をテストするには、JavaScriptへ事前コンパイルが必要。
Jestは、.vue
ファイルをコンパイルしない。
vue-jest
がインストールされていると、package.json
にjestフィールドを追加して.vue
をコンパイルの対象を指定できる。
"jest": { "moduleFileExtensions": [ "js", // vueファイルをコンパイルするように指示 "vue" ], // 単一ファイルコンポーネントをインポートするためのエイリアス // vue-cliプロジェクトで一般的な@をsrcへ割り当てる "moduleNameMapper": { "^@/(.*)$": "<rootDir>/src/$1" }, "transform": { // ES2015 機能をサポートするために必要 "^.+\\.js$": "<rootDir>/node_modules/babel-jest", // 単一ファイルコンポーネントをJavaScriptへ変換する ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest" }, // カスタムシリアライザを使用して読みやすいスナップショットを表示する // 改善されたスナップショットをJestのスナップとして渡す "snapshotSerializers": [ "<rootDir>/node_modules/jest-serializer-vue" ], // ソースマップを使用する "mapCoverage": true }
テストファイル
Jestは、.spec.js
, .test.js
の拡張子を持つ全てのテストファイルを再帰的に取得しテストを実行する。
公式で推奨しているのは、__test__
ディレクトリにテストファイルを置くように勧めている。
テストファイルは、自由に置くこともできるが、スナップショットテストが行われるとテストファイルと同階層に__snapshots__
ディレクトリが作成されることに注意。
import { shallow } from 'vue-test-utils' import { createRenderer } from 'vue-server-renderer' import List from '@/components/List.vue' describe('List.vue', () => { it('matches snapshot', () => { // ダミーのprops const items = ['item 1', 'item 2'] const renderer = createRenderer() // コンポーネントをマウント const wrapper = shallow(List, { propsData: { items } }) // コンポーネントをHTML文字列にレンダリング renderer.renderToString(wrapper.vm, (err, str) => { if (err) throw new Error(err) // 最新のスナップショットと一致するか比較 expect(str).toMatchSnapshot() }) }) })
テスト実行
package.json
を見てみると、以下の様にnpm-scriptsでtest
コマンドを介してjest
実行が定義されあった。
"scripts": { // 省略 "test": "jest" },
テストを実行してみる。
$ npm test
マークアップに変更があれば、Jestはテスト実行時にスナップショットに変更があることを以下の様に警告する。
FAIL __test__/List.test.js ● List.vue › matches snapshot expect(value).toMatchSnapshot() Received value does not match stored snapshot 1. - Snapshot + Received <ul> <li> - item 1 + item </li> <li> item 2 </li> </ul> at __test__/List.test.js:23:19 at Object.<anonymous> (__test__/List.test.js:21:14) at process._tickCallback (internal/process/next_tick.js:109:7) › 1 snapshot test failed.
意図的にスナップショットを変更したい場合は、スナップショットを再生成するオプションを付けてjest
を実行する。
$ ./node_modules/.bin/jest -u
また、--coverrage
オプションを付けてテストを実行すると、簡単にカバレッジも出すことも出来る。
$ ./node_modules/.bin/jest --coverage
以下、表示結果。
$ ./node_modules/.bin/jest --coverage PASS test/Message.spec.js PASS test/List.spec.js PASS test/MessageToggle.spec.js Test Suites: 3 passed, 3 total Tests: 5 passed, 5 total Snapshots: 1 passed, 1 total Time: 7.036s Ran all test suites. -------------------|----------|----------|----------|----------|----------------| File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines | -------------------|----------|----------|----------|----------|----------------| All files | 100 | 100 | 100 | 100 | | Message.js | 100 | 100 | 100 | 100 | | MessageToggle.vue | 100 | 100 | 100 | 100 | | -------------------|----------|----------|----------|----------|----------------|
他にもたくさんのオプションが用意されているので、-h
オプションで見つけて色々と試してみるといいだろう。
$ ./node_modules/.bin/jest -h
まとめ
vue-test-utils程ではないが、 Jestもドキュメントの和訳が進んでいて導入のハードルは低いように思った。
JestのassertionAPIは、スナップショットテスト以外にもたくさんある。
Jasmineと似ている様なのでとっつきやすいのかもしれない。