【TypeScript】ユニットテストにおける型のモック手法 - 型安全なテストの実践
2024-11-10
2024-11-10
概要
TypeScript
でユニットテストを行う際、外部依存を排除するためにモック(Mock)を使用することが一般的です。特に、API呼び出しやデータベースアクセスといった依存を含む関数をテストする際にモックは欠かせませんが、TypeScript
ではモックにも型安全性を求められます。型定義に基づいたモックの作成方法と、Jest
を使用した具体的な実装例を通して、TypeScript
の型安全なモック手法を紹介します。
モックとは?
モックは、テスト対象の関数やメソッドから外部依存を取り除き、テストに必要なデータや挙動を模倣するための擬似オブジェクトです。これにより、特定の関数やモジュールが想定通りに動作するかを検証しやすくなり、エラーの原因を特定しやすくなります。
モックの型安全性
TypeScript
でモックを作成する際、型定義に基づいて正確にモックを設定することが重要です。型安全なモックを作成することで、テスト時に誤ったデータや挙動を防ぎ、保守性の高いテストコードを実現できます。
TypeScriptでの型安全なモックの作成方法
型キャストを使ったモックの作成
型キャストを使うと、モックを型安全に実装できます。たとえば、UserService
というインターフェースがある場合、部分的にモックを作成する際に型キャストで型を調整します。
interface UserService {
getUserById(id: string): Promise<User>;
}
const userMock: UserService = {
getUserById: jest.fn().mockResolvedValue({ id: "123", name: "Alice" }),
};
ここでは、jest.fn().mockResolvedValue
を使ってgetUserById
の返り値を設定しています。TypeScript
の型推論に基づいて、型安全なモックが作成されます。
Partial
を用いた一部モックの作成
すべてのプロパティやメソッドを含む必要がない場合、Partial
を使って一部のモックを作成できます。これにより、不要なプロパティを省略しつつ、型整合性を保つことができます。
const partialUserMock: Partial<UserService> = {
getUserById: jest.fn().mockResolvedValue({ id: "123", name: "Bob" }),
};
// 型キャストでUserService型として利用可能に
const userServiceMock = partialUserMock as UserService;
このように、Partial
を使ってモックの型を柔軟に設定し、必要な部分のみモック化することで型安全なコードを維持できます。
jest.Mocked
による完全なモックの作成
Jestのjest.Mocked
型を使うと、インターフェースやクラスのメソッドをすべてモック関数に置き換えた型安全なモックを生成できます。
const userServiceMock: jest.Mocked<UserService> = {
getUserById: jest.fn().mockResolvedValue({ id: "456", name: "Charlie" }),
};
jest.Mocked
を使うことで、関数やメソッドが自動的にモック化され、jest.fn
を適用した型安全なモックオブジェクトを利用できます。
Jestでの型安全なモック作成の具体例
モジュールのモック化
Jestではjest.mock
を使ってモジュール全体をモックすることができます。jest.mock
を使うと、モジュールのすべての関数がモック関数に置き換えられるため、APIクライアントやデータベースアクセスなどの外部依存を簡単に模倣できます。
実装例
例えば、apiClient
モジュールのfetchUser
関数をモック化してテストする方法です。
// apiClient.ts
export const fetchUser = async (id: string): Promise<User> => {
// 実際のAPI呼び出しロジック
};
// userService.ts
import { fetchUser } from "./apiClient";
export const getUser = async (id: string): Promise<User> => {
return await fetchUser(id);
};
テストコードでfetchUser
をモック化してテストします。
// userService.test.ts
import { getUser } from "./userService";
import * as apiClient from "./apiClient";
// モジュールのモック化
jest.mock("./apiClient");
describe("getUser", () => {
it("should return a user by ID", async () => {
const mockUser = { id: "789", name: "Dave" };
(apiClient.fetchUser as jest.Mock).mockResolvedValue(mockUser);
const user = await getUser("789");
expect(user).toEqual(mockUser);
expect(apiClient.fetchUser).toHaveBeenCalledWith("789");
});
});
jest.mock
を使用し、fetchUser
関数を型安全にモック化しています。このようにすることで、fetchUser
が外部に依存しない形でテストできます。
モック関数で引数と戻り値の型を指定する
Jestのjest.fn
には、引数や戻り値の型を指定することができます。これにより、モック関数がテスト内でどのように動作するかをより詳細に制御できます。
interface Logger {
log: (message: string) => void;
}
const loggerMock: jest.Mocked<Logger> = {
log: jest.fn((message: string) => console.log(message)),
};
loggerMock.log("Test message");
expect(loggerMock.log).toHaveBeenCalledWith("Test message");
jest.fn
の引数に型を指定することで、モック関数の引数や戻り値が型安全になり、テストの精度が向上します。
APIクライアントのテストでの型安全なモック
APIクライアントをテストする際、レスポンスデータやエラーハンドリングの型を定義することで、型安全にテストできます。たとえば、axios
を使ってデータ取得関数をモック化する際に型定義を適用します。
// apiClient.ts
import axios from "axios";
import { User } from "./types";
export const fetchUser = async (id: string): Promise<User> => {
const response = await axios.get<User
>(`https://api.example.com/users/${id}`);
return response.data;
};
テストコードでaxios.get
をモックし、型安全なレスポンスを返すように設定します。
// apiClient.test.ts
import axios from "axios";
import { fetchUser } from "./apiClient";
import { User } from "./types";
jest.mock("axios");
const mockedAxios = axios as jest.Mocked<typeof axios>;
describe("fetchUser", () => {
it("should return a user object", async () => {
const mockUser: User = { id: "1", name: "Alice", email: "alice@example.com" };
mockedAxios.get.mockResolvedValue({ data: mockUser });
const user = await fetchUser("1");
expect(user).toEqual(mockUser);
expect(mockedAxios.get).toHaveBeenCalledWith("https://api.example.com/users/1");
});
});
ここではjest.Mocked<typeof axios>
でaxios
の型をモック化し、型安全にaxios.get
のレスポンスを設定しています。これにより、APIクライアントが外部の影響を受けずにテスト可能です。
型安全なモック作成のベストプラクティス
- インターフェースや型エイリアスを活用する
モックを作成する際、インターフェースや型エイリアスを利用することで、モックが本来の型と一致しているかを保証しやすくなります。 Partial
や型キャストで柔軟に対応
不要なプロパティを省略するためにPartial
や型キャストを活用し、必要な部分だけをモックすることでテストが簡潔になります。jest.Mocked
で型安全にモジュールをモック化
Jestのjest.Mocked
を使ってモジュール全体をモック化することで、関数やメソッドの型安全性を維持しつつ、外部依存を排除できます。- 実行時の型検証ライブラリ(Zodやio-tsなど)の併用
TypeScript
のコンパイル時の型チェックに加え、Zodなどの型検証ライブラリで実行時にも型安全性を保つと、さらに信頼性が向上します。
まとめ
TypeScript
でのユニットテストにおけるモックの型安全性は、コードの信頼性と保守性を向上させる重要な要素です。インターフェースや型キャスト、jest.Mocked
を活用して型安全なモックを作成し、エラーの少ないテストコードを実現しましょう。JestとTypeScript
の機能を活かして、テストの精度と効率を高める型安全なユニットテストを構築してください。
参照:
Jest公式ドキュメント