>웹 프론트엔드 >JS 튜토리얼 >모의품 가져오기에서 MSW까지: 테스트 여정

모의품 가져오기에서 MSW까지: 테스트 여정

Patricia Arquette
Patricia Arquette원래의
2024-12-03 11:47:09749검색

From Fetch Mocks to MSW: A Testing Journey

촉매제: 무고한 Axios 리팩터링

그것은 충분히 순수하게 시작되었습니다. "Axios를 사용하도록 이러한 가져오기 호출을 리팩터링하겠습니다." "무엇이 잘못될 수 있지?"라고 생각했습니다. 알고 보니, 특히 제가 세심하게 제작한 모든 가져오기 모형이 갑자기 초콜릿 찻주전자만큼 유용해졌습니다.

Axios에 대한 모든 모의를 다시 작성하는 대신 이 기회를 이용하여 접근 방식을 현대화하기로 결정했습니다. 모의근로자(MSW)를 입력하세요.

기존 방식: Jest Mocks 및 Fetch

이전에 내 테스트는 다음과 같았습니다.

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가 실제 세계에서 어떻게 작동하는지 나타내지 못했습니다. 실제 동작보다는 구현 세부 사항을 테스트하고 있었습니다.

MSW 입력: 더 나은 조롱 방법

모의 서비스 워커(MSW)는 API 모의에 근본적으로 다른 접근 방식을 취합니다. 함수 호출을 모의하는 대신 네트워크 수준에서 실제 네트워크 요청을 가로챕니다. 이는 다음과 같은 몇 가지 이유로 엄청난 규모입니다.

  • 런타임 통합: MSW는 실제 HTTP 요청을 가로채는 방식으로 작동합니다. 즉, 코드가 프로덕션 환경에서와 동일하게 실행됩니다. 더 이상 모의 가져오기 또는 axios가 없습니다. 실제 API 호출은 변경되지 않고 실행됩니다.
  • API 우선 설계: 모의 함수를 생각하는 대신 실제 API를 미러링하는 모의 API 엔드포인트를 정의합니다. 이를 통해 더 나은 API 설계를 추진하고 테스트를 실제 엔드포인트에 맞춰 유지할 수 있습니다.
  • 요청/응답 충실도: 단순화된 모의 개체 대신 상태 코드, 헤더, 응답 본문 등 실제 HTTP 개념을 사용하여 작업할 수 있습니다. 이는 보다 현실적인 엣지 케이스를 포착할 수 있다는 의미입니다.

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처럼 동일한 위치에서 성공 사례와 오류 사례를 모두 처리할 수 있었습니다. 그것은 "아하!" 중 하나였습니다. 테스트를 통해 실제로 더 나은 디자인을 향해 나아가는 순간입니다.

배운 교훈

  1. 적절한 수준에서 모의: MSW를 사용하면 기능 수준이 아닌 네트워크 수준을 모의하여 테스트를 더욱 현실적이고 강력하게 만들 수 있습니다.
  2. 함수가 아니라 엔드포인트를 고려하세요. 개별 함수 호출보다는 API 엔드포인트를 중심으로 모의 구조를 구성하는 것이 실제 애플리케이션 동작을 더 잘 나타냅니다.
  3. 오류 발생 시 처리: 별도의 오류 핸들러 대신 엔드포인트 핸들러 자체 내에서 오류를 처리하세요. 실제 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 모형이 있습니다.

  • 성공 및 오류 사례 모두 처리
  • 현실적인 대응 구조 사용
  • 테스트 전반에 걸쳐 재사용 가능
  • 실질적으로 통합 문제 파악

다음은 무엇입니까?

기대되는 내용은 다음과 같습니다.

  • 네트워크 오류를 더욱 현실적으로 시뮬레이션
  • 종단 간 테스트를 위해 MSW의 브라우저 통합 사용
  • 테스트 로딩 상태에 응답 지연 추가

때때로 최선의 개선은 강요된 변화에서 비롯됩니다. 간단한 Axios 리팩토링으로 시작된 것이 결국 훨씬 더 나은 테스트 아키텍처로 이어졌습니다. 이것이 바로 리팩토링의 핵심이 아닐까요?


이 글은 원래 제 블로그에 게재된 글입니다. 전체 스택 개발, 테스트 및 API 설계에 대한 자세한 내용을 보려면 저를 팔로우하세요.

위 내용은 모의품 가져오기에서 MSW까지: 테스트 여정의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.