앞서 포스팅에서 봤듯이, react 환경에서 vitest 테스트 라이브러리를 도입하여 테스트 코드를 작성하였다. 이제는 msw를 이용하여 api를 백엔드 없이 모킹을 이용하여 테스트하는 방법을 작성해보겠다. 항상 프론트엔드 UI를 개발할 때 api나 데이터가 준비되어 있지 않은 상황이 있을 수 있다. 그런 상황에서는 더미 데이터를 만들어 사용하는 방법도 있겠지만, 실제로 네트워크 요청을 하는 것과 비슷한 MSW를 도입하는 것이 더 좋은 방법이라고 판단하였다.
MSW는 API 요청을 모킹(mocking)하여 테스트 환경을 구축할 수 있도록 도와주는 라이브러리다. 실제 네트워크 요청 대신 가짜 응답을 제공함으로써, 외부 API에 의존하지 않고도 애플리케이션을 테스트할 수 있다.
네트워크 요청 모킹
MSW는 브라우저의 Fetch API와 XMLHttpRequest를 가로채어, 실제 서버와의 통신을 대신하여 가짜 응답을 제공한다. 이를 통해 외부 API에 의존하지 않고도 애플리케이션을 개발하고 테스트할 수 있다.
개발 및 테스트 환경 지원
MSW는 개발과 테스트 두 가지 환경 모두에서 활용할 수 있다. 개발 중에도 API 요청을 모킹하여, 실제 API가 준비되지 않았거나 응답이 느린 상황에서도 개발을 진행할 수 있다.
사용자 정의 응답
MSW는 다양한 상황에 맞춘 사용자 정의 응답을 설정할 수 있다. 요청의 URL, HTTP 메서드, 요청 헤더 및 본문에 따라 반환할 응답을 세밀하게 조정할 수 있어, 복잡한 테스트 시나리오를 쉽게 구현할 수 있다.
테스트 자동화 통합
MSW는 Jest, Vitest와 같은 테스트 프레임워크와 쉽게 통합할 수 있어, 테스트 코드 내에서 API 모킹을 손쉽게 설정하고 활용할 수 있다. 이를 통해 자동화된 테스트 환경을 구축할 수 있다.
npm install msw --save-dev
혹은
yarn add msw --dev
MSW를 사용하기 위해 핸들러와 서비스 워커를 설정해야 한다. 나는 다음과 같은 파일 구조를 택했다.
/src
├── mocks
├── data
└── data.js
├── browser.js
├── handlers.js
├── server.js
└── node.js
모킹할 API 요청을 정의한다. 예를 들어, 간단한 GET 요청을 모킹하는 핸들러를 작성할 수 있다.
import { http, HttpResponse } from 'msw'
import { matchingPostsData } from './api/data/matchingPostsData'
export const handlers = [
//룸메이트 매칭글 전체 조회
http.get('/api/v1/matchingposts', () => {
return HttpResponse.json(matchingPostsData)
}),
//베스트 룸메이트 조회
http.get('/api/v1/best-roommate-matchingposts', () => {
return HttpResponse.json(matchingPostsData)
}),
//내가 올린 매칭글 조회
http.get('/api/v1/my-matchingposts', () => {
return HttpResponse.json(matchingPostsData)
}),
...
]```
### 서비스 워커 설정 (browser.js)
서비스 워커를 설정하여 정의된 핸들러에 따라 api 호출을 가로채고 응답을 반환한다. 클라이언트 측의 네트워크 요청을 모킹하고, 개발 및 테스트 환경에서의 일관성을 확보할 수 있다.
```javascript
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'
export const worker = setupWorker(...handlers)
핸들러를 사용하여 MSW 서버를 설정한다.
setupServer는 MSW 라이브러리가 Node.js 환경에서 테스트를 위한 Mock Service Worker 서버를 설정하는 데 사용되는 함수이다. API 요청을 모킹하고, 테스트 중에 필요한 핸들러를 등록하는 데 매우 유용하다.
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
// 설정한 핸들러를 사용하여 MSW 서버 생성
export const server = setupServer(...handlers);
프로젝트의 진입점에서 서비스 워커를 등록한다. 보통 index.js 또는 App.js 파일에서 수행한다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
async function enableMocking() {
if (process.env.NODE_ENV !== 'development') {
return
}
const { worker } = await import('./mocks/browser')
return worker.start()
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
enableMocking().then(() => {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
})
나는 enabledMocking 함수를 이용하여 개발 환경일 경우에만 모킹을 활성화시켰다. 개발 환경일 경우 동적으로 browser.js를 임포팅하여 worker를 가져오고 이를 실행하도록 했다.
테스트 환경에서 MSW를 사용할 때는 각 테스트가 실행되기 전에 서버를 시작하고, 테스트가 끝난 후에는 서버를 종료해야 한다.
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import { expect, test, beforeAll, afterAll, afterEach } from "vitest";
import { server } from '../../mocks/node';
import RoommateSwiperList from './RoommateSwiperList';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
// MSW 서버를 테스트 전에 시작하고, 테스트 후에 종료합니다.
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
const wrapper = ({children}) => (
<QueryClientProvider client={queryClient}>
<MemoryRouter>
{children}
</MemoryRouter>
</QueryClientProvider>
);
describe('RoommateSwiperList api 모킹 데이터 불러오기', () => {
test('매칭글 api 데이터 불러오기', async () => {
render(<RoommateSwiperList type='new'/>, { wrapper });
// findBy 메소드는 getBy + waitFor의 조합
const displayedData = await screen.findByText('김혁수');
expect(displayedData).toBeInTheDocument();
});
});
모든 테스트 실행
npx test
특정 파일 테스트 실행
npx test RoommateSwiperList.test.jsx

이로써 모킹을 이용하여 api 호출을 통해 가져온 데이터에 대한 테스트가 성공적으로 완료된 것을 확인할 수 있었다!