목차
나는 프로젝트를 하면서 테스트 코드를 작성하는 미션을 받았고 msw 세팅을 한 상태에서
테스트 코드를 작성할 프로그램을 고르기 시작했다.
jest와 vitest중 멀 쓰지?
처음에 제안한 것이 바로 jest! 이것은 근데 우리가 기존 세팅을 vite로 해서 세팅하는데
배보다 배꼽이 더 크다는 생각이 하면 할 수록 들어 검색을 해본 결과..
vite쓸꺼면 Jest는 쓰지말기
요새 유행하는 번들러 환경 Vite에, 가장 유명한 테스팅 라이브러리 Jest를 엮어쓰려고 했으나 Jest는 웹팩 기반 라이브러리라 Vite환경에서 쓰려면 매우 복잡한 설정들을 거쳐야 한다. 아래 글에서
bumang.blog
결론은 vite 에서는 import.meta.env.VITE_XXX 와 같이 환경 변수를 사용하는데
WEBPACK 환경에서 위와 같은 환경 변수를 사용하기 위해서는 별도의 babel 관련 설정이 추가되어야 한다는 것이었다.
그래서 얼른 vitest로 바꾸어 진행을 했다.
그렇게 순조롭게 진행되는 줄 알았으나 msw 모킹을 하는 부분에서 문제가 생겼다.
다음과 같은 오류가 떴다...
src/hooks/queries/test/clubs.test.tsx [ src/hooks/queries/test/clubs.test.tsx ]
Error: No known conditions for "./browser" specifier in "msw" package
Plugin: vite:import-analysis
File: C:/Users/sins0/Desktop/Projects/mokkoji-fe/src/mocks/browsers.ts:1:28
1 | import { setupWorker } from "msw/browser";
| ^
2 | import { handlers } from "./handlers";
3 | export const worker = setupWorker(...handlers);
❯ e node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:15470:25
❯ n node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:15470:650
❯ o node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:15470:1293
❯ resolveExportsOrImports node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:16673:18
❯ resolveDeepImport node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:16686:25
❯ tryNodeResolve node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:16516:18
❯ ResolveIdContext.resolveId node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:16289:19
❯ EnvironmentPluginContainer.resolveId node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:47553:17
❯ TransformPluginContext.resolve node_modules/vitest/node_modules/vite/dist/node/chunks/dep-ByPKlqZ5.js:47756:15
vitest와 msw는 따로 사용
그 중에서도 특히 우리가 자세히 볼 부분은 오프라인 경험을 생성하고 네트워크 요청을 가로채는
역할을 할 수 있다는 점이다.
그래서 우리는 모킹한 데이터 외에 임시 데이터를 또 만들어 주어 통합테스트와 유닛테스트를
하기로 결정하였다.
또한 테스트 부분은 로직이 그래도 복잡하다고 볼 수 있는 api를 다루는 부분을 중심으로 진행했다.
clubList.test.tsx
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import { setupServer } from "msw/node";
import { MemoryRouter } from "react-router-dom";
import { http, HttpResponse } from "msw";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import ClubList from "./ClubList";
const server = setupServer(
http.get("/clubs", () => {
// 클라이언트에서 `/clubs` 경로로 요청하면, 아래의 JSON 데이터를 반환함
return HttpResponse.json({
data: {
clubs: [
{
id: 1,
name: "그리디",
category: "학술/교양",
affiliation: "중앙동아리",
description: "간단 소개",
recruitStartDate: "2025-10-12",
recruitEndDate: "2025-10-13",
imageUrl: "https://example.com/images/greedy.jpg",
favorite: true,
},
],
pagination: {
page: 1,
size: 9,
totalPages: 3,
totalElements: 27,
},
},
});
})
);
beforeAll(() => server.listen());
afterAll(() => server.close());
const queryClient = new QueryClient();
describe("ClubList", () => {
it("api로부터 전달받은 데이터를 ClubBox 컴포넌트를 통해 화면에 랜더링해야한다", async () => {
// ClubList 컴포넌트 렌더링
render(
<MemoryRouter>
<QueryClientProvider client={queryClient}>
<ClubList />
</QueryClientProvider>
</MemoryRouter>
);
// ClubBox 의 내용 확인
await waitFor(() => screen.queryByText("그리디"));
expect(screen.queryByText("그리디"));
expect(screen.queryByText("간단 소개"));
expect(screen.queryByText("학술/교양"));
expect(screen.queryByText("중앙동아리"));
// 동아리 로고 이미지
const image = screen.queryByAltText("그리디");
expect(image);
// 즐겨찾기 버튼
expect(screen.getByRole("button", { name: /favoriteButton/i }));
});
it("api 호출 실패", async () => {
server.use(
http.get("/clubs", () => {
return new HttpResponse(null, { status: 500 });
})
);
});
});

'코딩 정보 > React' 카테고리의 다른 글
[React] 에서의 에러 핸들링 전략에 대해 살펴보자 (0) | 2025.04.10 |
---|---|
리액트 리팩토링 정리글 (2) | 2025.03.28 |
[React][Zustand] SessionStorage를 사용해 보자 (0) | 2024.12.08 |
[React][Vite] vite-plugin-svgr을 사용해 보자 (0) | 2024.12.06 |
[React] vite 빌드 시 config에 대해 알아보고 경로 설정을 해보자 (0) | 2024.12.06 |