それは無邪気に始まりました。 「これらのフェッチ呼び出しをリファクタリングして、Axios を使用するだけです。何が問題になる可能性があるでしょうか?」と私は考えました。結局のところ、かなりの部分、具体的には、私が慎重に作成したフェッチ モックがすべて、突然チョコレート ティーポットと同じくらい便利になりました。
Axios 用にすべてのモックを再構築するのではなく、この機会を利用してアプローチを最新化することにしました。モック サービス ワーカー (MSW) を入力します。
以前のテストは次のようになっていました:
const mockFetch = vi.fn(); global.fetch = mockFetch; describe("API functions", () => { beforeEach(() => { mockFetch.mockReset(); }); test("fetchTrips - should fetch trips successfully", async () => { const mockTrips = [{ id: 1, name: "Trip to Paris" }]; mockFetch.mockResolvedValueOnce({ ok: true, json: async () => mockTrips, }); const trips = await fetchTrips(mockSupabase); expect(trips).toEqual(mockTrips); }); });
うまくいきましたが、まったくエレガントではありませんでした。各テストには手動のモック設定が必要でしたが、モックは脆弱で、実際の API が現実世界でどのように動作するかを実際には表していませんでした。実際の動作ではなく、実装の詳細をテストしていました。
Mock Service Worker (MSW) は、API モックに対して根本的に異なるアプローチを採用しています。関数呼び出しをモックする代わりに、実際のネットワーク リクエストをネットワーク レベルでインターセプトします。これはいくつかの理由から非常に重要です:
これらの同じテストが MSW でどのように見えるかは次のとおりです:
// Your API handler definition http.get(`${BASE_URL}/trips`, () => { return HttpResponse.json([ { id: "1", location: "Trip 1", days: 5, startDate: "2023-06-01" }, { id: "2", location: "Trip 2", days: 7, startDate: "2023-07-15" }, ]); }); // Your test - notice how much cleaner it is test("fetchTrips - should fetch trips successfully", async () => { const trips = await fetchTrips(); expect(trips).toEqual([ { id: "1", location: "Trip 1", days: 5, startDate: "2023-06-01" }, { id: "2", location: "Trip 2", days: 7, startDate: "2023-07-15" }, ]); });
テストごとに手動でモックをセットアップする必要はなくなり、MSW ハンドラーがすべて処理します。さらに、これらのハンドラーは多くのテストで再利用できるため、重複が減り、テストがより保守しやすくなります。
MSW のセットアップは驚くほど簡単だったので、すぐに疑問に思いました。テストにおいてこれほど簡単なことはありません...
beforeAll(() => { server.listen({ onUnhandledRequest: "bypass" }); }); afterEach(() => { server.resetHandlers(); cleanup(); }); afterAll(() => { server.close(); });
次に、実際に私の API に似たハンドラーを作成します。
export const handlers = [ http.get(`${BASE_URL}/trips`, () => { return HttpResponse.json([ { id: "1", location: "Trip 1", days: 5, startDate: "2023-06-01" }, { id: "2", location: "Trip 2", days: 7, startDate: "2023-07-15" }, ]); }), ];
エラー処理に対する私の最初の試みは...まあ、楽観的だったとしましょう:
export const errorHandlers = [ http.get(`${BASE_URL}/trips/999`, () => { return new HttpResponse(null, { status: 404 }); }), ];
問題は?より一般的な /trips/:id ハンドラーが最初にすべてをキャッチしていました。これは、Express アプリで特定のルートの前に包括的なルートを設定するようなものでした。初歩的なミスです。
何度か頭を悩ませてテストに失敗した後、より良いアプローチはルート自体内でエラーを処理することであることに気付きました。
const mockFetch = vi.fn(); global.fetch = mockFetch; describe("API functions", () => { beforeEach(() => { mockFetch.mockReset(); }); test("fetchTrips - should fetch trips successfully", async () => { const mockTrips = [{ id: 1, name: "Trip to Paris" }]; mockFetch.mockResolvedValueOnce({ ok: true, json: async () => mockTrips, }); const trips = await fetchTrips(mockSupabase); expect(trips).toEqual(mockTrips); }); });
このパターンが現れました。個別のエラー ハンドラーの代わりに、実際の API と同じように、成功ケースとエラー ケースの両方を同じ場所で処理できます。それは「ああ!」というものでした。テストによって実際に優れた設計に向かう瞬間です。
最終的なセットアップは、より保守しやすく、より現実的で、実際の問題を把握するのに役立ちます。
の時代は終わりました。
// Your API handler definition http.get(`${BASE_URL}/trips`, () => { return HttpResponse.json([ { id: "1", location: "Trip 1", days: 5, startDate: "2023-06-01" }, { id: "2", location: "Trip 2", days: 7, startDate: "2023-07-15" }, ]); }); // Your test - notice how much cleaner it is test("fetchTrips - should fetch trips successfully", async () => { const trips = await fetchTrips(); expect(trips).toEqual([ { id: "1", location: "Trip 1", days: 5, startDate: "2023-06-01" }, { id: "2", location: "Trip 2", days: 7, startDate: "2023-07-15" }, ]); });
代わりに、次のような適切な API モックがあります。
楽しみにしていること:
時には、変化を強いられることで最良の改善が得られることもあります。単純な Axios リファクタリングとして始まったものは、最終的にははるかに優れたテスト アーキテクチャにつながりました。それがリファクタリングの本質ではないでしょうか?
この記事はもともと私のブログに公開されたものです。フルスタック開発、テスト、API 設計に関する詳細なコンテンツについては、こちらをフォローしてください。
以上がフェッチ モックから MSW まで: テストの旅の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。