この記事はJestのドキュメントを和訳したものです。また、定期的に更新される予定です。
はじめに
Painless JavaScript Testing
Jestのホームページでは、次の3点をJestの特徴としています。
- Easy Setup
- Instant Feedback
- Snapshot Testing
自分なりにまとめると、JestはJavaScriptで記述された様々なコードを簡単にテストでき、テスト実行時には前回の保存から変更があったものだけをチェックするように最適化されていて、時系列に沿った状態の変遷まで可視化することができる、ということでしょうか。ぜひマスターしたいものです。
目次
- Getting Started
- Snapshot Testing
- Testing React Apps
1. Getting Started
以下のリンクの和訳が中心となっています。
https://facebook.github.io/jest/docs/getting-started.html
npmを利用してインストールを行います。
$ npm install --save-dev jest
上述の--save-dev
オプションを利用することで、テストを行うプロジェクトにあるpackage.json
にJestがリストアップされます。
それでは、二つの数字の和を求めるように設計された関数をテストしてみます。以下のようにsum.js
を作成します。
function sum(a, b) {
return a + b;
}
module.exports = sum;
module.exports
とは、コードが記述されているファイルをmodule
として扱い、exports
したオブジェクトをrequire
キーワードを利用して呼び出すことができるようにするための記述です。
それでは、sum.test.js
という名前のファイルを作成し、テストコードを書いてみましょう。
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
})
上記のテストコードを実行するために、package.json
に以下の設定を追加してください。
"scripts": {
"test": "jest"
}
それでは、登録したコマンドを実行してみましょう。
$ npm test
PASS ./sum.test.js
✔︎ adds 1 + 2 to equal 3(5ms)
このようにして、テストコードを作成し、実行することができます。
個人的な感想ですが、テストコードがすごく読みやすいですね。'expect sum of 1 and 2 is expected to be 3'のように、英語の文法そのものになるように設計されているみたいです。
2. Snapshot Testing
以下のリンクの翻訳が主となっています。
https://facebook.github.io/jest/docs/snapshot-testing.html
2.1 Snapshot Testingとは?
スナップショットテストとは、作成したUIが予期せずに変化してしまわないか確認するための優れたツールのことを指します。
Jestを利用してスナップショットテストを行う
典型的なスナップショットテストでは、レンダリングされたUIのスクリーンショットを撮り、テストコードで指定したイメージと比較して、同様であるかどうか確かめるといったことが行われます。
Reactコンポーネントのテストにおいても同様のアプローチを取ることができそうです。UIをグラフィカルにレンダリングする代わりに、テスト用のレンダラーをReactのコンポーネントツリーを対象にシリアライズ可能な形式で出力します。これにより、逐一アプリケーション全体を組み立てる必要がなくなり、リソースを消費しないテストが可能になります。
以下のようにして、Link
コンポーネントをテストしてみましょう。
なお、コンポーネント自体のコードは次のリンクにあります。
import React from "react";
import Link from "../Link.react";
import renderer from "react-test-renderer";
it('renders correctly', () => {
const tree = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>
).toJSON();
expect(tree).toMatchSnapshot();
});
最初にこのテストコードを実行した時に、Jestは以下のようなsnapshot file
を生成します。
exports[`Link renders correctly 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}>
Facebook
</a>
スナップショットはコードの変更に伴って生成されるでしょう。Jestではpretty-format
を利用し、人間が読みやすい形式でコードのレビューを示してくれます。はじめのスナップショットテスト以降、テストそのものは、その一つ手前のテストとのシンプルな比較となります。それらが合致した場合、テストは成功とみなされます。もしも合致しなかった場合は、テストランナーが修正すべきバグを通知してくれるか、スナップショットが更新される必要があることを教えてくれます。
Updating Snapshots
バグが発見された場合に、スナップショットテストが失敗するのは明らかです。そのような場合は、ふたたびテストが通るように修正してください。
テストが成功するスナップショットそのものの更新を行いたい場合は、以下のコマンドを実行してください。
$ jest --updateSnapshot
3. Testing React Apps
Facebookでは、ReactのテストにJestを利用しています。
個人的に一番知りたいのがReactのテストの方法でした。気合いを入れて書きます。
また、ここでは既存のReactアプリケーションをテストする場合について書きます。
Setup
既存のアプリケーションがある場合は、テストコードを動かすために幾つかのパッケージをインストールする必要があります。以下のコマンドを実行してください。
$ npm install --save-dev jest babel-jest babel-preset-es2015 babel-preset-react react-test-renderer
コマンドを実行後、package.json
に正しくパッケージがインストールされていることを確認してください。
確認ができましたか?テストをする準備は万全です。
React ComponentのSnapshot Testing
ハイパーリンクをレンダリングするLinkコンポーネントのスナップショットテストを作成しましょう。
import React from 'react';
const STATUS = {
NORMAL: 'normal',
HOVERED: 'hovered',
};
export default class Link extends React.Component {
constructor(props) {
super(props);
this._onMouseEnter = this._onMouseEnter.bind(this);
this._onMouseLeave = this._onMouseLeave.bind(this);
this.state = {
class: STATUS.NORMAL,
};
}
_onMouseEnter() {
this.setState({class: STATUS.HOVERED});
}
_onMouseLeave() {
this.setState({class: STATUS.NORMAL});
}
render() {
return (
<a
className={this.state.class}
href={this.props.page || '#'}
onMouseEnter={this._onMouseEnter}
onMouseLeave={this._onMouseLeave}>
{this.props.children}
</a>
);
}
それでは、テストコードを書いていきましょう。
import React from 'react';
import Link from '../Link.react';
import renderer from 'react-test-renderer';
test('Link changes the class when hovered', () => {
const component = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props.onMouseEnter();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props.onMouseLeave();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
npm test
を実行すれば、以下のような内容のファイルを生成します。
import React from 'react';
import Link from '../Link.react';
import renderer from 'react-test-renderer';
test('Link changes the class when hovered', () => {
const component = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props.onMouseEnter();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props.onMouseLeave();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
次回テストを実行した際には、出力されたスナップショットは、以前作成されたスナップショットと比較されます。
jest -u
を実行することで、スナップショットを上書きすることができます。