この記事では、Jestのセットアップ、マッチャー、テストランナー、モックなどの基礎について詳しく解説します。
Jestを使うプロジェクトに入ったばかりの方や、Jestの基礎や使い方が知りたい方は、ぜひ参考にしてみてください。
Jestは、JavaScriptのテストフレームワークであり、フロントエンド開発で広く使われています。
Jestは使いやすく、統合された環境を提供し、高速なテストの実行が可能です。
また、モック・スパイの作成やスナップショットテストなどの便利な機能も備えています。
これらの特徴により、Jestはフロントエンドのテストを効率的かつ信頼性高く実行します。
JavaScriptやTypeScriptで書かれたコードであれば、そのほとんどはJestを使ってテストをおこなえます。
Jestを利用するためには、プロジェクトにJestをインストールする必要があります。
以下のコマンドを使用して、プロジェクトにJestを追加します。
npm install --save-dev jest
yarn add --dev jest
pnpm add --save-dev jest
describeは、JestにおいてTest Suite(テストスイート)を作成するための関数です。
テストスイートは関連するテストケースをグループ化し、テストコードの組織化と管理を容易にします。
describe(テストスイートの説明, テストスイートの実行するコールバック関数)
testは、Jestにおいて単一のTest Case(テストケース)を定義するための関数です。
テストケースは、特定の条件やシナリオに対して実行される単一のテスト単位です。
test(テストケースの説明, テストケースの実行するコールバック関数)
これらを用いることで、多くのテストでは以下の様な形式でおこなわれます。
describe('四則演算', () => {
test('足し算', () => {
const result = add(2, 3);
expect(result).toBe(5);
})
test('引き算', () => {
const result = subtract(5, 2);
expect(result).toBe(3);
})
})
Jestは、beforeEachとafterEachという2つの特殊な関数を提供しています。
これらの関数を使用することで、各テストケースの前後に特定の処理を追加することができます。
beforeEach関数は、各テストケースが動く前に実行されるコードブロックを定義するために使用されます。
例えば、以下のようなテストスイートがあるとします。
describe('四則演算', () => {
let calculator;
beforeEach(() => {
calculator = new Calculator();
});
it('足し算', () => {
expect(calculator.add(2, 3)).toBe(5);
});
});
上記の例では、Calculatorというクラスのインスタンスを各テストケースの前に作成しています。
これにより、各テストケースが動く前に新しいインスタンスが初期化され、テスト間で状態が共有されないようになります。
afterEach関数は、各テストケースが動いた後に実行されるコードブロックを定義するために使用されます。
例えば、以下のようなテストスイートがあるとします。
describe('カウント', () => {
let counter;
beforeEach(() => {
counter = new Counter();
});
afterEach(() => {
counter.reset();
});
it('増加', () => {
counter.increment();
expect(counter.value()).toBe(1);
});
it('減少', () => {
counter.decrement();
expect(counter.value()).toBe(-1);
});
});
上記の例では、Counterというクラスのインスタンスを各テストケースの前に作成し、各テストケースの後にカウンターをリセットしています。
これにより、各テストケースは独立して実行され、カウンターの初期状態が保持されることが保証されます。
Jestでは、マッチャーを使用することでテストの結果を期待値と比較し、テストの成功または失敗を判断します。
expect(実際の値).適切なMatcher(期待する値);
以下に一般的なマッチャーの具体的な例を10個示します。
expect(2 + 2).toBe(4);
expect([1, 2, 3]).toEqual([1, 2, 3]);
expect(1).toBeTruthy();
expect(0).toBeFalsy();
expect(null).toBeNull();
expect(undefined).toBeUndefined();
expect(1).toBeDefined();
expect([1, 2, 3]).toContain(2);
expect(5).toBeLessThan(10);
expect(10).toBeGreaterThan(5);
Jestのモック機能は、テスト中に関数やモジュールの振る舞いを模擬するために使用されます。
これにより、外部の依存関係やリソースへのアクセスを制御し、テストを予測可能で独立した状態で実行することができます。
モック機能を使用すると、モック関数を作成し、その関数の振る舞いを定義することができます。
以下にモック機能の一般的な使用例を示します。
以下のようなmath.jsモジュールがあるとします。
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
これを使用する関数calculateがあるとします。
// calculate.js
import { add, subtract } from './math';
export function calculate(a, b) {
return add(a, b) * subtract(a, b);
}
この場合、math.jsの関数をモックすることで、calculate関数のテストが容易になります。
以下はそのテストの例です。
// calculate.test.js
import { calculate } from './calculate';
import * as math from './math';
// mathモジュールをモックする
jest.mock('./math');
test('calculate関数のテスト', () => {
// add関数をモックする
math.add.mockReturnValue(10);
// subtract関数をモックする
math.subtract.mockReturnValue(5);
// calculate関数を呼び出す
const result = calculate(3, 2);
// 期待する結果は (10 * 5) = 50
expect(result).toBe(50);
// add関数が呼び出されたかを検証
expect(math.add).toHaveBeenCalledWith(3, 2);
// subtract関数が呼び出されたかを検証
expect(math.subtract).toHaveBeenCalledWith(3, 2);
});
このテストでは、Jestを使用してcalculate関数をテストするために、モジュールのモックを作成しています。
以下に詳細を説明します。
// calculate.test.js
import { calculate } from './calculate';
import * as math from './math';
import { calculate } from ‘./calculate’;で、./calculateからcalculate関数をインポートしています。
import * as math from ‘./math’;で、./mathモジュールをmathという名前のオブジェクトとしてインポートしています。
これにより、mathオブジェクトを介してmath.jsモジュール内の関数にアクセスすることができます。
// mathモジュールをモックする
jest.mock('./math');
jest.mock(‘./math’);という行で、./mathモジュールをモックしています。
これにより、テスト中では実際のmath.jsモジュールの代わりに、テスト用のモック関数を使用することができます。
// calculate.test.js
test('calculate関数のテスト', () => {
// add関数をモックする
math.add.mockReturnValue(10);
// subtract関数をモックする
math.subtract.mockReturnValue(5);
// calculate関数を呼び出す
const result = calculate(3, 2);
// 期待する結果は (10 * 5) = 50
expect(result).toBe(50);
// add関数が呼び出されたかを検証
expect(math.add).toHaveBeenCalledWith(3, 2);
// subtract関数が呼び出されたかを検証
expect(math.subtract).toHaveBeenCalledWith(3, 2);
});
math.add.mockReturnValue(10);で、mathオブジェクトのadd関数をモックし、そのモック関数が返す値を10に設定しています。
同様に、math.subtract.mockReturnValue (5);で、mathオブジェクトのsubtract関数をモックし、そのモック関数が返す値を5に設定しています。
その後、calculate関数を呼び出し、その結果をconst result = calculate(3, 2);で受け取ります。
最後に、expectステートメントを使用して、結果が期待通りの値であることや、add関数とsubtract関数が正しく呼び出されたことを検証しています。
次のようなモジュールがあるとします。
// api.js
export async function fetchData() {
// リモートAPIからデータを取得する処理
}
これを使用する関数getDataがあるとします。
// getData.js
import { fetchData } from './api';
export async function getData() {
const data = await fetchData();
// 取得したデータを加工する処理
return data;
}
この場合、api.jsのfetchData関数をモックすることで、外部のリモートAPIへの依存を排除してテストできます。
以下はそのテストの例です。
// getData.test.js
import { getData } from './getData';
import * as api from './api';
// apiモジュールをモックする
jest.mock('./api');
test('getData関数のテスト', async () => {
// fetchData関数をモックする
api.fetchData.mockResolvedValue('テストデータ');
// getData関数を呼び出す
const result = await getData();
// 期待する結果は 'テストデータ'
expect(result).toBe('テストデータ');
// fetchData関数が呼び出されたかを検証
expect(api.fetchData).toHaveBeenCalled();
});
このコードは、Jestを使用してgetData関数をテストするためのテストスクリプトです。
以下に詳細を説明します。
// getData.test.js
import { getData } from './getData';
import * as api from './api';
import { getData } from ‘./getData’;で、./getDataからgetData関数をインポートしています。
import * as api from ‘./api’;で、./apiモジュールをapiという名前のオブジェクトとしてインポートしています。
これにより、apiオブジェクトを介してapi.jsモジュール内の関数にアクセスすることができます。
// apiモジュールをモックする
jest.mock('./api');
jest.mock(‘./api’);で、./apiモジュールをモックしています。
これにより、テスト中では実際のapi.jsモジュールの代わりに、テスト用のモック関数を使用することができます。
test('getData関数のテスト', async () => {
// fetchData関数をモックする
api.fetchData.mockResolvedValue('テストデータ');
// getData関数を呼び出す
const result = await getData();
// 期待する結果は 'テストデータ'
expect(result).toBe('テストデータ');
// fetchData関数が呼び出されたかを検証
expect(api.fetchData).toHaveBeenCalled();
});
api.fetchData.mockResolvedValue (‘テストデータ’);で、apiオブジェクトのfetchData関数をモックし、そのモック関数が解決された値として’テストデータ’を返すように設定しています。
const result = await getData();でgetData関数を呼び出し、その結果をresultという変数に代入しています。
expectステートメントを使用して、結果が期待通りの値であることや、fetchData関数が呼び出されたことを検証しています。
素のjavascriptやletなどはあまり使用するべきではないですが、あくまで簡易的な説明のため、本記事では使用しています。
以下にユーザーの情報とログイン状態を管理する関数のテストを作成します。
// user.js
function User(name) {
this.name = name;
this.isLoggedIn = false;
this.login = function () {
this.isLoggedIn = true;
};
this.logout = function () {
this.isLoggedIn = false;
};
}
export default User;
上記のコードでは、User という関数を定義しています。
User 関数はコンストラクタとして利用され、name と isLoggedIn のプロパティがインスタンスに設定されます。
login 関数と logout 関数は、それぞれインスタンスメソッドとして定義され、ログイン状態を制御します。
次に、作成したクラスのユニットテストをおこないます。
Jestを使用してテストファイル user.test.js を作成し、以下のようなテストケースを記述します。
// user.test.js
import User from './user';
describe('ユーザー', () => {
let user;
beforeEach(() => {
user = new User('John');
});
afterEach(() => {
user.logout();
});
test('名前の設定', () => {
expect(user.name).toBe('John');
});
test('ログイン前', () => {
expect(user.isLoggedIn).toBe(false);
});
test('ログイン済み', () => {
user.login();
expect(user.isLoggedIn).toBe(true);
});
test('ログアウト', () => {
user.login();
user.logout();
expect(user.isLoggedIn).toBe(false);
});
});
test('名前の設定', () => {
expect(user.name).toBe('John');
});
名前の設定テスト: user.nameが’John’と等しいことを確認しています。
テストケース内では、new User(‘John’)でUserクラスの新しいインスタンスを作成し、その名前を検証しています。
test('ログイン前', () => {
expect(user.isLoggedIn).toBe(false);
});
ログイン前テスト:user.isLoggedInがfalseであることを確認しています。
ログイン前のUserインスタンスの状態をテストしています。
test('ログイン済み', () => {
user.login();
expect(user.isLoggedIn).toBe(true);
});
ログイン済みテスト: user.isLoggedInがtrueであることを確認しています。
テストケース内でuser.login()を呼び出してUserインスタンスをログイン状態にし、その状態を検証しています。
test('ログアウト', () => {
user.login();
user.logout();
expect(user.isLoggedIn).toBe(false);
});
ログアウトテスト: user.isLoggedInがfalseであることを確認しています。
テストケース内でuser.login()を呼び出してUserインスタンスをログイン状態にし、user.logout()を呼び出してログアウトさせ、その状態を検証しています。
beforeEach(() => {
user = new User('John');
});
afterEach(() => {
user.logout();
});
beforeEach()では、テストケースごとに新しいUserインスタンスを作成し、afterEach()ではログアウト処理をおこなっています。
これにより、各テストケースは独立して実行され、インスタンスの状態が相互に影響を与えないようになります。
今回は、Jestを使用したフロントエンドのテストの基礎について説明しました。
こちらの記事を見てJestを少しでも理解していただき、実践に役立てていただければと思います。
クランチタイマーでは、他にもフロントエンドの開発に役立つ記事を書いていますので、よろしければ併せてご覧ください。
SHARE:
お気軽にお問い合わせください。
TEL082-299-2286
代表の佐々⽊が⽉に1回お届けするメールマガジン。
国内外スタートアップの最新情報や最新技術のサマリー、クランチタイマーの開発事例紹介など、ITに関する役⽴つ情報を中⼼にお送りします!