JavaScriptのテスティングフレームワークのJestを5分でキャッチアップ

この記事では、Jestの基本的な使い方をざっくり説明します。Jestの主要機能を確認したい、Jestの雰囲気をつかみたいといった方向けです。Jestをより詳しく知りたい場合は、公式ドキュメントをご確認ください。

Jestとは

  • Jestはシンプルさを重視したJavaScriptのテスティングフレームワークです。
  • 哲学として「テストを楽しくする」が掲げられています。
  • Jestは、TypeScript, Node, React, Angular, Vueなどさまざまなプロジェクトで利用できます。

Jestの特徴は次の通りです。

  • 設定なしで導入できる
  • テストを並列で実行するので速い
  • モックが簡単
  • フラグを追加するだけでコードカバレッジを計測できる

Jestでテストを書く

  • describeメソッドでいくつかの関連するテストをまとめます。
  • そして、testメソッド(別名のitメソッドもある)で、テストケースを記載します。
  • テストケース内では、expectとMatcherを使ってアサーションをします。
  • MatcherはtoBe()toEqual()などテスト対象の値を比較をするメソッドです。
// sum.js
// テスト対象のコード
function sum(a, b) {
  return a + b;
}
module.exports = sum;

// sum.test.js
// テストコード
const sum = require('./sum');

describe('sum', () => {
  it('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });

  it('adds -1 + 2 to equal 1', () => {
    expect(sum(-1, 2)).toBe(1);
  });
});

JestのMatcherを使いこなす

  • Jestではexpectで検証します。
  • .notをメソッドチェインすることで否定を表せます。
  • さまざまな種類のMatcherがあるのでテストケースの実装で困ることはほぼないでしょう。
  • 参考: Jestで利用できるMatchers
describe('sum', () => {
  it('should not return null', () => {
    expect(sum(1, 2)).not.toBeNull();
  });
});

Jestで非同期処理のテストコードを書く

  • Jestで非同期処理のテストをする方法はいくつかありますが、async/await を使うと良いでしょう。
  • 他のテストの書き方としては、then/catchでテストする方法、.resolves/.rejectsでテストする方法などがあります。

testの関数をasyncキーワードで定義し、awaitで非同期処理を待ちます。

// Async/Awaitで非同期処理をテスト

// テスト対象の非同期処理のコード
const fetchUser = async (id) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // テストのためのダミー実装
      if (id === 1) {
        resolve('John Doe');
      } else {
        reject('not found user');
      }
    }, 200);
  });
}

// テストコード
describe('fetchUser', () => {
  // 非同期処理が成功するケース
  test('the data is John Doe', async () => {
    const user = await fetchUser(1);
    expect(user).toBe('John Doe');
  });

  // 非同期処理が失敗するケース
  test('the fetch fails with an error', async () => {
    await expect(fetchUser(2)).rejects.toMatch('not found user');
  });
});

参考: Jestで非同期コードのテストの書き方

Jestでモックを使う

  • モックする方法は、「テストコード中でモック関数を作成する方法」と「マニュアルモックを使う方法」の2つがあります。
  • ここでは、「テストコード中でモック関数を作成する方法」を説明します。
  • モジュールのモックをクリアするには、Jestの設定ファイルでclearMockstrueにすると良いでしょう。

Jestのモックはjest.fn()で作成し、mockReturnValue()mockImplementation()でモックの返り値の指定やモックの振る舞いを実装できます。

// jest.fn()でモックを作成
// mockReturnValue()でモックの返り値を指定できる
test('the mock returns 10', () => {
  const myMock = jest.fn().mockReturnValue(10);
  expect(myMock()).toBe(10);
});

// mockReturnValueOnce()で呼び出し回数に応じてモックの返り値を変えることができる
test('the mock returns 10, "x", false', () => {
  const myMock = jest.fn()
    .mockReturnValueOnce(10)
    .mockReturnValueOnce('x')
    .mockReturnValueOnce(false);

  expect(myMock()).toBe(10);
  expect(myMock()).toBe('x');
  expect(myMock()).toBe(false);
});

// mockImplementation()でモックの振る舞いを実装できる
test('the mock adds a and b', () => {
  const myAddMock = jest.fn()
    .mockImplementation((a, b) => a + b);

  expect(myAddMock(1, 3)).toBe(4);
});

また、外部のモジュールをモックしたい場合はjest.mock()を使います。

// users.js
import axios from 'axios';

class Users {
  static all() {
    return axios.get('/users.json').then(resp => resp.data);
  }
}
export default Users;

// users.test.js
import axios from 'axios';
import Users from './users';

jest.mock('axios');

it('should fetch users', async () => {
  const users = [{ name: 'Bob' }];
  const resp = { data: users };
  axios.get.mockResolvedValue(resp);
  // もしくは mockImplementationで次のように実装可能
  // axios.get.mockImplementation(() => Promise.resolve(resp))

  const data = await Users.all()
  expect(data).toEqual(users);
});

参考: マニュアルモックモック関数

テストのSetupとTeardown

  • beforeEach()で各テストケースのセットアップ処理を共通化することができます。
  • afterEach()で各テストケースの終了後の処理を共通化することができます。
  • beforeAll()afterAll()を使うことでテストケースのまとまりに応じてセットアップや終了後の処理を記載できます。
describe('isCity', () => {
  // 各テストケースの実行前に実行される
  beforeEach(() => {
    initializeCityDatabase();
  });

  // 各テストケースの終了後に実行される
  afterEach(() => {
    clearCityDatabase();
  });

  test('city database has Vienna', () => {
    expect(isCity('Vienna')).toBeTruthy();
  });

  test('city database has San Juan', () => {
    expect(isCity('San Juan')).toBeTruthy();
  });
});

その他の使い方