서버로 부터 api를 호출하여 데이터를 받아오는 것을 mocking을 활용하여 처리하는 방법
모든 테스트에서는 api를 실제 호출하기 보다 요청을 모의하여 데이터를 가져올 수 있다.
이를 통해 사용할 수 없는 백엔드로 인한 비정상적인 테스트를 막을 수 있고,
모의 에러를 이용한 에러의 대응, 의도된 여러가지 응답에 대한 처리를 리액트 컴포넌트 안에서 테스트로 확인할 수 있다.
실제 request, response에 소모되는 시간낭비도 방지할 수 있다.
서버로의 request를 모의 데이터로 처리하는 방법 경우 2가지
1. fecth
2. axios
fetch
fetch api의 모의 데이터를 받아오는 방법을 jest function 함수를 이용한 수동 moking 방법과 라이브러리를 이용한 방법
모의 데이터에 의존하는 리액트 컴포넌트를 테스트 할 수 있는 방법
1. 수동
- fetch object를 jest function 이용하여 교체 후 컴포넌트 렌더링
- api 에러케이스는 에러를 발생시켜서 처리 가능
// 정상 case
it("render Mock User data", async () => {
// Promise.resolve를 반환하는 jest function 함수를 fetch에 대응
// Promise.resolve 함수 내에서는 json property가 있고
// 얘도 Promise를 resolve하고 resolve 된 값으로는
// 사전에 정의한 모킹된 user data를 반환한다.
// 실제 fetch함수가 호출되어서 반환되는 값의 구조를 맞춰준것
global.fetch = jest.fn(() => Promise.resolve({
json: () => Promise.resolve(mockUserData)
})) as jest.Mock
render(<User id='123' />)
expect(fetch).toHaveBeenCalledTimes(1)
const name = await screen.findByText(`이름 : ${mockUserData.name}`)
expect(name).toBeInTheDocument()
const age = await screen.findByText(`나이 : ${mockUserData.age}`)
expect(age).toBeInTheDocument()
const addr = await screen.findByText(`주소 : ${mockUserData.address}`)
expect(addr).toBeInTheDocument()
})
// 에러 case
it("render API error", async () => {
global.fetch = jest.fn(() => {throw new Error('unauthorized')}) as jest.Mock
render(<User id='123' />)
expect(fetch).toHaveBeenCalledTimes(1)
expect(screen.getByRole('heading')).toBeInTheDocument()
})
2. 라이브러리
- jest-fetch-mock 패키지 사용
- 설정과 해지 적용
beforeAll(() => fetchMock.enableMocks())
beforeEach(() => {
fetchMock.resetMocks()
})
afterAll(() => fetchMock.disableMocks())
// 정상 case
it("render Mock User data", async () => {
fetchMock.mockResponse(JSON.stringify(mockUserData))
render(<User id='123' />)
expect(fetch).toHaveBeenCalledTimes(1)
const name = await screen.findByText(`이름 : ${mockUserData.name}`)
expect(name).toBeInTheDocument()
const age = await screen.findByText(`나이 : ${mockUserData.age}`)
expect(age).toBeInTheDocument()
const addr = await screen.findByText(`주소 : ${mockUserData.address}`)
expect(addr).toBeInTheDocument()
})
// 에러 case
it("render API error", async () => {
fetchMock.mockReject(() => {throw new Error('unauthorized')})
render(<User id='123' />)
expect(fetch).toHaveBeenCalledTimes(1)
expect(screen.getByRole('heading')).toBeInTheDocument()
})
axios
- axios-mock-adaptor 사용
- axios를 파라미터로 전달하여 MockAdaptor 생성
import MockAdapter from 'axios-mock-adapter'
const mockAxios = new MockAdapter(axios)
const mockProductData = {
id: '123',
name: 'test product',
price: 1000,
desc: 'product desc',
}
afterEach(() => {
mockAxios.reset()
})
// 정상 case
it("render Mock Product data", async () => {
mockAxios.onGet('/api/product/123').reply(200, mockProductData)
render(<Product id='123' />)
expect(mockAxios.history.get.length).toBe(1)
const name = await screen.findByText(`이름 : ${mockProductData.name}`)
expect(name).toBeInTheDocument()
const age = await screen.findByText(`가격 : ${mockProductData.price}`)
expect(age).toBeInTheDocument()
const addr = await screen.findByText(`설명 : ${mockProductData.desc}`)
expect(addr).toBeInTheDocument()
})
// 에러 case
it("render API error", async () => {
mockAxios.onGet('/api/product/123').reply(401, 'unauthorized')
render(<Product id='123' />)
expect(mockAxios.history.get.length).toBe(1)
// API 실패 이후 DOM update 대기
await waitFor(() => expect(screen.getByRole('heading')).toBeInTheDocument());
})
axios-mock-adaptor 의 메소드
- replyOnce: 한 번만 mocking, chaining 가능 (순서대로 mock response)
- onAny: http method 모두 대응
- reset: mock 핸들러 모두 제거 (afterEach 안에 callback)
- restore: 모킹했던 값에서 실제 API 요청 날아가게 원복시켜줌
'개발공부 > React' 카테고리의 다른 글
[React] CRA 없이 리액트 앱 만들기: 목적과 프로젝트 세팅 (4) | 2024.10.12 |
---|---|
[React] 테스팅 패턴 - 외부 모듈의 Mocking을 이용한 검증 (0) | 2024.07.31 |
[React] 테스팅 패턴 - Rendering 검증 (0) | 2024.07.21 |
[React] 테스팅 패턴 - AAA 패턴 (0) | 2024.07.02 |
[React] 테스팅 패턴 - 설정과 해지 (0) | 2024.07.01 |