BLOG

ブログ

  • Top
  • Blog
  • 初心者でもわかる!Jestフロントエンドユニットテストの基礎

初心者でもわかる!Jestフロントエンドユニットテストの基礎

ブログサムネイル
Crunchtimer株式会社

著者:クランチタイマー株式会社

Twitter Icon

クランチタイマーはアプリ・Webサービスなどの開発を手掛けるWebデベロッパーです。ブログ記事ではロジカルで最適な手法を伝え、課題解決の手助けとなる情報を惜しみなく提供していきます。



この記事では、Jestのセットアップ、マッチャー、テストランナー、モックなどの基礎について詳しく解説します。

Jestを使うプロジェクトに入ったばかりの方や、Jestの基礎や使い方が知りたい方は、ぜひ参考にしてみてください。

開発技術について定期的に情報収集したいという方は、ぜひクランチタイマーのニュースレターにご登録ください。

月に1回程度、おすすめ記事や更新情報などをメールでお届けいたします。

Jestとは

Jestは、JavaScriptのテストフレームワークであり、フロントエンド開発で広く使われています。

Jestは使いやすく、統合された環境を提供し、高速なテストの実行が可能です。

また、モック・スパイの作成やスナップショットテストなどの便利な機能も備えています。

これらの特徴により、Jestはフロントエンドのテストを効率的かつ信頼性高く実行します。

JavaScriptやTypeScriptで書かれたコードであれば、そのほとんどはJestを使ってテストをおこなえます。

Jestのインストール

Jestを利用するためには、プロジェクトにJestをインストールする必要があります。

以下のコマンドを使用して、プロジェクトにJestを追加します。

npmを使った方法

npm install --save-dev jest

yarnを使った方法

yarn add --dev jest

pnpmを使った方法

pnpm add --save-dev jest

Jestの基本概念

describe

describeは、JestにおいてTest Suite(テストスイート)を作成するための関数です。

テストスイートは関連するテストケースをグループ化し、テストコードの組織化と管理を容易にします。

describe(テストスイートの説明, テストスイートの実行するコールバック関数)

test

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

beforeEach関数は、各テストケースが動く前に実行されるコードブロックを定義するために使用されます。

例えば、以下のようなテストスイートがあるとします。

describe('四則演算', () => {

  let calculator;

  beforeEach(() => {

    calculator = new Calculator();

  });

  it('足し算', () => {

    expect(calculator.add(2, 3)).toBe(5);

  });

});

上記の例では、Calculatorというクラスのインスタンスを各テストケースの前に作成しています。

これにより、各テストケースが動く前に新しいインスタンスが初期化され、テスト間で状態が共有されないようになります。

afterEach

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というクラスのインスタンスを各テストケースの前に作成し、各テストケースの後にカウンターをリセットしています。

これにより、各テストケースは独立して実行され、カウンターの初期状態が保持されることが保証されます。

Matcher(マッチャー)

マッチャーとは

Jestでは、マッチャーを使用することでテストの結果を期待値と比較し、テストの成功または失敗を判断します。

マッチャーの使い方

expect(実際の値).適切なMatcher(期待する値);

マッチャーの例

以下に一般的なマッチャーの具体的な例を10個示します。

    • 1.toBe : 厳密な等値性を検証します。
expect(2 + 2).toBe(4);
    • 2.toEqual : 値の内容を比較します。(オブジェクトなどで用いられる)
expect([1, 2, 3]).toEqual([1, 2, 3]);
    • 3.toBeTruthy : 値が真の値(truthy)であることを検証します。
expect(1).toBeTruthy();
    • 4.toBeFalsy :値が偽の値(falsy)であることを検証します。
expect(0).toBeFalsy();
    • 5.toBeNull : 値がnullであることを検証します。
expect(null).toBeNull();
    • 6.toBeUndefined : 値がundefinedであることを検証します。
expect(undefined).toBeUndefined();
    • 7.toBeDefined : 値がundefinedでないことを検証します。
expect(1).toBeDefined();
    • 8.toContain : 配列や文字列が特定の要素を含んでいることを検証します。
expect([1, 2, 3]).toContain(2);
    • 9.toBeLessThan : 値が指定した値より小さいことを検証します。
expect(5).toBeLessThan(10);
    • 10.toBeGreaterThan : 値が指定した値より大きいことを検証します。
expect(10).toBeGreaterThan(5);

Mock(モック)

モックとは

Jestのモック機能は、テスト中に関数やモジュールの振る舞いを模擬するために使用されます。

これにより、外部の依存関係やリソースへのアクセスを制御し、テストを予測可能で独立した状態で実行することができます。

モックの具体的な使い方

モック機能を使用すると、モック関数を作成し、その関数の振る舞いを定義することができます。

以下にモック機能の一般的な使用例を示します。

(1)モジュールのモック

以下のような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関数をテストするために、モジュールのモックを作成しています。

以下に詳細を説明します。

1.関数とモジュールをインポートする

// 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モジュール内の関数にアクセスすることができます。

2.mathモジュールをモックする
// mathモジュールをモックする

jest.mock('./math');

jest.mock(‘./math’);という行で、./mathモジュールをモックしています。

これにより、テスト中では実際のmath.jsモジュールの代わりに、テスト用のモック関数を使用することができます。

3.各関数をモックし検証する
// 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関数が正しく呼び出されたことを検証しています。

(2)外部モジュールのモック

次のようなモジュールがあるとします。

// 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関数をテストするためのテストスクリプトです。

以下に詳細を説明します。

1.getData関数、apiモジュールをインポートする
// 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モジュール内の関数にアクセスすることができます。

2.apiモジュールをモックする

// apiモジュールをモックする
jest.mock('./api');

jest.mock(‘./api’);で、./apiモジュールをモックしています。

これにより、テスト中では実際のapi.jsモジュールの代わりに、テスト用のモック関数を使用することができます。

3.fetchData関数のモック、getData関数の呼び出しと検証
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を少しでも理解していただき、実践に役立てていただければと思います。

開発技術について定期的に情報収集したいという方は、ぜひクランチタイマーのニュースレターにご登録ください。

月に1回程度、おすすめ記事や更新情報などをメールでお届けいたします。

この記事をシェア  

link-icon

筆者

中井祐一郎

クランチタイマー株式会社で主にフロントエンドの開発をしているエンジニアです。最近の好きな名言は「初めて牡蠣を食った人間は大胆な人間であった」です。ブログでは、主に言語や技術について解説させていただいてます。

最新情報を確認する

CONTACT

お気軽にお問い合わせください。

TEL082-299-2286

NEWSLETTER

代表の佐々⽊が⽉に1回お届けするメールマガジン。
国内外スタートアップの最新情報や最新技術のサマリー、クランチタイマーの開発事例紹介など、ITに関する役⽴つ情報を中⼼にお送りします!