bkdragon's log

[TEST] MSW와 통합 테스트 본문

concept

[TEST] MSW와 통합 테스트

bkdragon 2023. 7. 12. 21:51

MSW는 서비스 워커(Service Worker)를 사용하여 네트워크 호출을 가로채는 API 모킹(mocking) 라이브러리이다. 백엔드 api가 만들어지기 전에 mock 데이터를 이용해 통합 테스트를 해 볼 수 있다. 브라우저 환경 노드환경 모두 사용할 수 있다.

 

서비스 워커

서비스 워커는 웹 응용 프로그램, 브라우저, 그리고 (사용 가능한 경우) 네트워크 사이의 프록시 서버 역할한다. 서비스 워커의 개발 의도는 여러가지가 있지만, 그 중에서도 효과적인 오프라인 경험을 생성하고, 네트워크 요청을 가로채서 네트워크 사용 가능 여부에 따라 적절한 행동을 취하고, 서버의 자산을 업데이트할 수 있다. 또한 푸시 알림과 백그라운드 동기화 API로의 접근도 제공한다.

 

설치 및 세팅

설치

npm install msw --save-dev
# or
yarn add msw --dev

 

노드환경과 브라우저환경 모두에서 사용할 수 있는 세팅을 정리하려고 한다.

src/mocks/brower.ts 생성

import { setupWorker } from 'msw'
import { handlers } from './handler'

export const worker = setupWorker(...handlers)

 

src/mocks/server.ts 생성

import { setupServer } from 'msw/node'
import { handlers } from './handler'

export const server = setupServer(...handlers)

 

src/mocks/handler.ts 생성

import { rest } from 'msw'
export const handlers = [
    return rest.get(url, (req, res, ctx) => {
            return res(ctx.status(200), ctx.json(data))
    })
]

 

핸들러는 url, http 메서드 별로 나눌 수 있다. 직접 작성한 예시로 tasks를 받아오는 핸들러이다. tasks의 내용은 임으로 정하면 된다.

const getTasks = () => {
    return rest.get('https://localhost:3000/tasks', (_, res, ctx) => {

        return res(ctx.status(200), ctx.json(tasks))
    })
}

 

setUpTest.ts 파일에 server 전처리 추가

 import "@testing-library/jest-dom"; // 기존에 jest 세팅할 때 이미 있는 import
import { server } from "./mocks/server"; // 위에서 생성한 server (node 환경)

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

 

 

브라우저 환경의 경우 개발 모드에만 mock 데이터를 받게 처리해줘야 한다. index.ts 파일에 아래 내용을 추가

if (process.env.NODE_ENV === 'development') {
  const { worker } = require('./mocks/browser')
  worker.start()
}

이렇게 하면 개발 모드 브라우저와 노드 환경에서 api 통신을 탈취해 미리 handler에서 정해둔 mock 데이터를 반환한다 이를 통해 통합 테스트를 진행할 수 있는데 일부러 에러를 발생시켜서 에러 관련 테스트도 진행할 수 있다.

 

테스트 코드

describe('todoContainer', () => {
    const renderTodoContainer = () =>
        render(
            <TodoContainer />
        )

    context('데이터를 불러오는 api 호출을 할 때', () => {
        context('에러가 발생하지 않았다면', () => {
            it('데이터가 화면에 렌더링 되어야 한다..', async () => {
                renderTodoContainer()

                const task = await screen.findByText('자기')

                await waitFor(() => {
                    expect(task).toBeInTheDocument()
                })
            })
        })

        context('에러가 발생했다면', () => {
            it('에러를 감지해야한다.', async () => {
                server.use(
                    rest.get('https://localhost:3000/tasks', (_, res, ctx) => {
                        return res(ctx.status(400), ctx.json({
                            errorMessage : 'Network error'
                        }) )
                    })
                )
                renderTodoContainer()

                const error = await screen.findByTestId('errorMessage')

                await waitFor(() => {
                    expect(error).toBeInTheDocument()
                })
            })
        })
    })
})

 

이제 TodoContainer 컴포넌에서 해당 api를 호출하는 코드를 작성하고 테스트코드와 일치하게 에러처리를 해주면 테스트에 통과할 것이다.

'concept' 카테고리의 다른 글

JavaScript 엔진이 await를 만났을 때  (0) 2023.07.18
Flux 패턴  (0) 2023.07.17
[TEST] 프론트의 단위 테스트와 BDD  (0) 2023.07.09
[Test] Jest, React-testing-library  (0) 2023.07.07
[JavaScript] Call Stack, Callback Queue  (0) 2023.06.24