はじめに
「テストを書きたいけれど、設定だけで半日溶けた」——そんな経験はないでしょうか。テストランナー、アサーションライブラリ、モックライブラリをそれぞれ選んでつなぎ込むのは、意外と骨が折れる作業です。
Jestは、この面倒な組み合わせ作業をほぼゼロにしてくれるJavaScript向けテスティングフレームワークです。インストールした瞬間からテストランナー・アサーション・モック・カバレッジ計測がひとつになって動き出すため、設定に時間を溶かすことなく「テストを書く」という本題に集中できます。
Jestとは
Jestは、Meta(旧Facebook)が開発したJavaScript / TypeScript向けのテスティングフレームワークです。React案件を中心に広く使われていますが、Node.jsのバックエンドコードやVue、Angularのプロジェクトでも問題なく利用できます。
主な特徴
- ゼロコンフィグで動く -
npm installしてすぐにdescribe・it・expectが使え、複雑な設定ファイルを書かなくてもテストが実行できます - モック機能が組み込み済み -
jest.fn()やjest.mock()により、外部関数や外部モジュールへの依存を簡単に差し替えられます - スナップショットテストに対応 - コンポーネントの出力やオブジェクトの構造を「前回との差分」として自動比較できます
- テストの並列実行 - 各テストファイルを別プロセスで並列実行するため、テストが増えても実行時間が伸びにくい仕組みになっています
インストール
npmでインストールする場合は以下のコマンドを実行します。
npm install --save-dev jest
yarnを使っている場合は次のとおりです。
yarn add --dev jest
package.jsonのscriptsにtestコマンドを登録しておくと、npm testだけでテストを実行できるようになります。
{
"scripts": {
"test": "jest"
}
}
基本的な使い方
テスト対象の関数を用意し、同じディレクトリに*.test.jsというファイル名でテストファイルを作成するのが基本の流れです。
// sum.js
function sum(a, b) {
return a + b
}
module.exports = sum
// sum.test.js
const sum = require('./sum')
test('1 + 2 は 3 になる', () => {
expect(sum(1, 2)).toBe(3)
})
npm testを実行すると、Jestが*.test.jsファイルを自動的に検出し、test()(またはit())で定義した内容を順番に実行してくれます。expect().toBe()のようなアサーションで期待値と実際の値を比較し、一致しなければテストが失敗として報告されます。
関連するテストが複数ある場合は、describeでグルーピングすると結果が見やすくなります。
describe('sum関数', () => {
test('正の数同士を足せる', () => {
expect(sum(1, 2)).toBe(3)
})
test('負の数を足すと減算になる', () => {
expect(sum(5, -3)).toBe(2)
})
})
実践的なユースケース
非同期処理のテスト
APIレスポンスやDBアクセスなど、非同期処理を含む関数のテストもJestは標準でサポートしています。async/awaitをそのままテスト関数内で使うことができます。
// fetchUser.js
async function fetchUser(id, api) {
const user = await api.getUser(id)
if (!user) throw new Error('user not found')
return user
}
module.exports = fetchUser
// fetchUser.test.js
const fetchUser = require('./fetchUser')
test('存在するユーザーを取得できる', async () => {
const mockApi = { getUser: async () => ({ id: 1, name: 'taro' }) }
const user = await fetchUser(1, mockApi)
expect(user.name).toBe('taro')
})
test('存在しないユーザーはエラーになる', async () => {
const mockApi = { getUser: async () => null }
await expect(fetchUser(999, mockApi)).rejects.toThrow('user not found')
})
expect().rejectsを使うことで、Promiseが reject されるケースまで自然に書けるのがポイントです。
モック関数でAPI呼び出しを検証する
外部サービスへのAPI呼び出しをテストのたびに本当に実行するわけにはいきません。jest.fn()でモック関数を作り、「何回呼ばれたか」「どんな引数で呼ばれたか」を検証できます。
// notify.js
function notify(sendMail, user) {
sendMail(user.email, 'ようこそ!')
}
module.exports = notify
// notify.test.js
const notify = require('./notify')
test('登録時にメール送信関数が正しい引数で呼ばれる', () => {
const sendMail = jest.fn()
const user = { email: 'test@example.com' }
notify(sendMail, user)
expect(sendMail).toHaveBeenCalledTimes(1)
expect(sendMail).toHaveBeenCalledWith('test@example.com', 'ようこそ!')
})
jest.mock('./api')のようにモジュール単位でモック化することもでき、外部モジュールに依存したコードでもテスト対象だけを切り出して検証できます。
スナップショットテストでUIの変化を検知する
Reactコンポーネントの出力やオブジェクトの構造など、「意図しない変化がないか」を確認したい場面ではスナップショットテストが便利です。初回実行時にJestが出力結果をファイルとして保存し、次回以降はその内容と比較します。
// user.test.js
function createUser(name) {
return { name, role: 'member', createdAt: '2026-01-01' }
}
test('createUserの出力がスナップショットと一致する', () => {
const user = createUser('hanako')
expect(user).toMatchSnapshot()
})
一度スナップショットが作られると、次に実行したときに出力が変わっていれば差分としてテストが失敗します。意図した変更であればjest --updateSnapshot(またはウォッチモード中のuキー)でスナップショットを更新できます。
まとめ
Jestは、テストランナー・アサーション・モック・スナップショットテストを1つのパッケージにまとめることで、テストを書き始めるまでのハードルを大きく下げてくれるフレームワークです。まずは1つの関数に対してtest()を1本書いてみるところから始めて、非同期処理のテストやモックを使った依存の切り離しへと少しずつ広げていくと、無理なくテスト文化を根付かせられるはずです。