メインコンテンツまでスキップ

Slice のテストを書く

この章では TypeScript のテストの書き方の学習も兼ねて、Redux Toolkit で作成した Slice の一部の機能のテストを書いていきたいと思います。

note

基本的には JavaScript でのテストコードの書き方と同じです。

JavaScript との違いは、TypeScript 用に追加でインストールするライブラリが少し追加されるくらいです。

テストに必要なライブラリをインストールする

今回はテストツールに「Jest」を使います。

以下のコマンドを実行して、TypeScript で Jest を使う準備をします。

npm install -D jest @types/jest ts-jest ts-node

Jest の設定ファイルを作成する

以下のコマンドを実行すると、Jest の設定ファイル jest.config.js(jest.config.ts) を作成するためのプロセスが開始します。

npx jest --init

? Would you like to use Typescript for the configuration file? › (y/N)
→yを打ってエンター(TypeScriptを使っているため)

? Choose the test environment that will be used for testing › - Use arrow-keys. Return to submit
→jsdom (browser-like)を選択してエンター(Webブラウザを使ったアプリとして開発をしているため)

? Do you want Jest to add coverage reports? › (y/N)
→特に何も入力せずエンター(デフォルトでは大文字が選択される。今回の場合はNが選択される(Nの意味はNo))

? Which provider should be used to instrument code for coverage? › - Use arrow-keys. Return to submit.
→v8を選択してエンター(今回はbabelを利用しないため(インストールしないため))

? Automatically clear mock calls, instances, contexts and results before every test? › (y/N)
→特に何も入力せずエンター(デフォルトのNが選択される)

一連のプロセスが終わると、プロジェクトのルートディレクトリに jest.config.ts が作成されます。(人によっては jest.config.js かもしれません)

info

自動生成されたjest.config.tsの中を見ると、コメントが大量にあって読みにくいので、コメント部分は削除しても良いです。

Jest の設定ファイルを修正する

自動生成されたjest.config.tsts-jestの記述を加えていきます。

jest.config.ts
export default = {
// [...]
// Replace `ts-jest` with the preset you want to use
// from the above list
preset: 'ts-jest',
}

Jest を実行してみる

まずは package.json の scripts に以下のコードを追記してください。こうすることで npm test で jest を実行できるようになります。

package.json
{
...,
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "jest"
},
...
}

上記の記述をした上で npm test を実行した際に以下のようなエラーが出たら、エラーメッセージに従って、 jest-environment-jsdom をインストールします。

jest-environment-jsdomエラー

jest-environment-jsdomをインストールする
npm i -D jest-environment-jsdom

jest-environment-jsdom をインストールした後、もう一度 npm test を実行し、以下のようにエラーが出なくなれば OK です。

jest-environment-jsdomインストール後

note

現状はテストファイルを 1 つも作成していないので、「No tests found, exiting with code 1」と出力されています。

テストファイルを作成する

テストファイルのファイル名にはルールがあります。

上の画像の「testMatch」と書かれている行の黄色文字を読んでいただくとわかりますが、以下のパターンのルールでファイル名を作成する必要があります。

  • **/__tests__/**/*.[jt]s?(x)
  • **/?(*.)+(spec|test).[tj]s?(x)

これらが意味するのは、以下のパターンにマッチするファイルテストにファイルになるということを意味しています。

テストファイル名のパターン
  • __tests__ディレクトリより下に作られた js, jsx, ts, tsx ファイル
  • __tests__ディレクトリ関係なく、末尾がファイルの test.js, test.jsx, test.ts, test.tsx, spec.js, spec.jsx, spec.ts, spec.tsx というファイル

今回は todosSlice.ts のテストファイルとして、todosSlice.tsと同じ階層に todosSlice.spec.ts というテストファイルを作成していきます。

テストファイルの中身

テストファイルの中身は以下のように記述してください。

src/features/todos/todosSlice.spec.ts
import todosReducer, { create, TodoState } from './todosSlice';import { TodoInput, TodoId } from './types';describe('todos reducer', () => {  const initialState: TodoState = {    todos: [],    displayStatus: 'all',    isFetching: false,    error: null,  };  it('should handle create reducer', () => {    const payload: TodoInput = {      title: 'title1',      body: 'body1',    };    const newState = todosReducer(initialState, create(payload));    const todos = newState.todos;    expect(todos.length).toEqual(1);    expect(typeof todos[0].id).toEqual('string');    expect(todos[0].title).toEqual(payload.title);    expect(todos[0].body).toEqual(payload.body);    expect(todos[0].status).toEqual('waiting');    expect(todos[0].createdAt).not.toEqual(undefined);    expect(todos[0].updatedAt).toEqual(undefined);    expect(todos[0].deletedAt).toEqual(undefined);    expect(newState).toEqual({      todos,      displayStatus: 'all',      isFetching: false,      error: null,    });  });});

このテストコードは reducer の create が実行されたときに、どのように State の内容が変化している確認しています。

具体的な確認内容は以下の通りです。

  • Todo データが 1 件追加されているかを確認している
  • 追加された Todo データの各プロパティの値や型が正しいかを確認している
  • TodoState の各値(displayStatus, isFetching, error)が正しいかを確認している

テストを実行する

npm testを実行すると、以下の画像のように uuidの部分でエラーが出ている可能性があります。

uuidに関するエラー

調べてみると、解決につながる以下の記事を見つけることができました。

こちらの記事を参考にすると、以下の対応をすることで解決したとのことなので、同じようにuuidの特定のバージョンをインストールするようにします。

uuid のバージョンを指定したら解決したとのこと
  • “@types/uuid”: “8.3.0", -> “@types/uuid”: “8.0.1",
  • “uuid”: “8.3.1", -> “uuid”: “8.1.0",

以下のコマンド実行してバージョンを同じ記事と同じものを使うようにします。

npm i -D @types/uuid@8.0.1
npm i uuid@8.1.0

上記を試しすとエラーが解決されるかと思います。(僕の環境ではエラーが解決されました)

info

もしエラーが解決されず、色々と調べても解決できない場合は Discord まで質問をいただけたらと思います。

もう一度テストを実行する

uuid のバージョン問題を解決した上でテストを再度実行すると、以下のようなエラーがでるかもしれません。

dayjsに関するエラー

エラーメッセージにある「(0 , dayjs_1.default) is not a function」で検索したところ、以下の stack overflow の記事を見つけました。

TypeError: (0 , dayjs_1.default) is not a function

ここに書かれている Answer に従って、 date.tsの 1 行目を以下のように書き換えます。

変更前のsrc/features/todos/utils/date.ts
import dayjs from 'dayjs';export type DateTime = string;export const getCurrentDateTime = (): DateTime => {  return dayjs().format('YYYY-MM-DD HH:mm:ss');};
変更後のsrc/features/todos/utils/date.ts
import * as dayjs from 'dayjs';export type DateTime = string;export const getCurrentDateTime = (): DateTime => {  return dayjs().format('YYYY-MM-DD HH:mm:ss');};

コードを書き換えた後で再度テストを実行すると問題なくテストが成功したのを確認できました。

テストが成功した様子

テストの実行が長いなと思った時

先ほどの成功したテストの結果の「Time」をみると、1 つのテストを実行するのに 4.112 秒かかっています。

このテスト実行時間を短くしたい場合は、esbuild-jestというのを使ってみるのも 1 つの手です。

note

esbuild-jest(GitHub レポジトリ)

esbuild-jest を使うための設定

基本的には「esbuild-jest(GitHub レポジトリ)」に書かれていることに従えば良いです。

まずは指示通りに以下のコマンドを実行して esbuild-jest esbuild をインストールします。

npm install --save-dev esbuild-jest esbuild

次も指示通りに、Jest の config ファイルを修正します。(5-7 行目のコードが、今回追加した部分)

jest.config.ts
export default {  coverageProvider: 'v8',  preset: 'ts-jest',  testEnvironment: 'jsdom',  transform: {    '^.+\\.tsx?$': 'esbuild-jest',  },};

インストール作業jest.config.tsの修正が完了したら npm testを実行してください。

そうすると以下のように再度dayjsでエラーになるかと思います。

esbuildの設定を追加してdayjsのエラーが再発した様子

dayjsの import 部分を、以下のように元の書き方に戻しておきます。(1 行目を元の書き方に戻す)

src/features/todos/utils/date.ts
import dayjs from 'dayjs';export type DateTime = string;export const getCurrentDateTime = (): DateTime => {  return dayjs().format('YYYY-MM-DD HH:mm:ss');};

元の書き方に戻した状態で再度 npm test を実行すると以下のようにテストが成功するかと思います。

esbuildで発生したdayjsのエラーが解決してテストが通った様子

また、テストにかかっている時間もご覧ください。

esbuild-jest を使わなかった時は 「4.112 秒」かかっていました。

しかし、esbuild-jestを使った場合「1.765 秒」になりました。(約 2.3 倍の差)