FE 통합 테스트 한계
JeongSeulho
2024년 01월 08일
준비중...
클립보드로 복사
0. 들어가며
배송 주문 페이지를 예시로 통합 테스트를 작성하고 그 한계를 정리
1. ItemList 컴포넌트 통합 테스트
import { screen, within } from '@testing-library/react';
import React from 'react';
import ItemList from '@/pages/purchase/components/ItemList';
import { mockUseCartStore } from '@/utils/test/mockZustandStore';
import render from '@/utils/test/render';
beforeEach(() => {
mockUseCartStore({
cart: {
6: {
id: 6,
title: 'Handmade Cotton Fish',
price: 100,
description:
'The slim & simple Maple Gaming Keyboard from Dev Byte comes with a sleek body and 7- Color RGB LED Back-lighting for smart functionality',
images: [
'https://user-images.githubusercontent.com/35371660/230712070-afa23da8-1bda-4cc4-9a59-50a263ee629f.png',
],
count: 3,
},
7: {
id: 7,
title: 'Awesome Concrete Shirt',
price: 50,
description:
'The Nagasaki Lander is the trademarked name of several series of Nagasaki sport bikes, that started with the 1984 ABC800J',
images: [
'https://user-images.githubusercontent.com/35371660/230762100-b119d836-3c5b-4980-9846-b7d32ea4a08f.png',
],
count: 4,
},
},
totalCount: 7,
totalPrice: 500,
});
});
it('구매 상품들의 이름, 수량, 금액이 순서대로 노출된다.', async () => {
await render(<ItemList />);
const rows = screen.getAllByRole('row');
const first = within(rows[0]);
const second = within(rows[1]);
expect(first.getByText('Handmade Cotton Fish')).toBeInTheDocument();
expect(first.getByText('3개')).toBeInTheDocument();
expect(first.getByText('$300.00')).toBeInTheDocument();
expect(second.getByText('Awesome Concrete Shirt')).toBeInTheDocument();
expect(second.getByText('4개')).toBeInTheDocument();
expect(second.getByText('$200.00')).toBeInTheDocument();
});
2. Payment 컴포넌트 통합 테스트
import { screen } from '@testing-library/react';
import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { NO_COUPON_ID } from '@/constants';
import Payment from '@/pages/purchase/components/Payment';
import { mockUseCartStore } from '@/utils/test/mockZustandStore';
import render from '@/utils/test/render';
beforeEach(() => {
mockUseCartStore({
cart: {
6: {
id: 6,
title: 'Handmade Cotton Fish',
price: 100,
description:
'The slim & simple Maple Gaming Keyboard from Dev Byte comes with a sleek body and 7- Color RGB LED Back-lighting for smart functionality',
images: [
'https://user-images.githubusercontent.com/35371660/230712070-afa23da8-1bda-4cc4-9a59-50a263ee629f.png',
],
count: 3,
},
7: {
id: 7,
title: 'Awesome Concrete Shirt',
price: 50,
description:
'The Nagasaki Lander is the trademarked name of several series of Nagasaki sport bikes, that started with the 1984 ABC800J',
images: [
'https://user-images.githubusercontent.com/35371660/230762100-b119d836-3c5b-4980-9846-b7d32ea4a08f.png',
],
count: 4,
},
},
totalCount: 7,
totalPrice: 500,
});
});
// react-hook-form 검증을 위해 FormProvider를 랩핑한 컴포넌트 생성
const TestPayment = (props = {}) => {
const methods = useForm({
defaultValues: {
name: 'leejaesung',
address: '',
phone: '',
requests: '',
coupon: NO_COUPON_ID,
...props,
},
});
return (
<FormProvider {...methods}>
<Payment />
</FormProvider>
);
};
it('총 상품 금액은 "$500.00"로 노출된다', async () => {
await render(<TestPayment />);
expect(screen.getByText('$500.00')).toBeInTheDocument();
});
it('배송비는 "$5.00"로 노출된다', async () => {
await render(<TestPayment />);
expect(screen.getByText('$5.00')).toBeInTheDocument();
});
it('할인 쿠폰을 선택하지 않은 경우 "선택 안함"으로 노출되며, 총 결제 금액은 "$505.00"로 노출된다', async () => {
await render(<TestPayment />);
expect(screen.getByText('선택 안함')).toBeInTheDocument();
expect(await screen.findByText('$505.00')).toBeInTheDocument();
});
it('price 타입의 쿠폰인 경우, 총 결제 금액은 "$502.00"로 노출된다', async () => {
await render(<TestPayment coupon={2} />);
expect(await screen.findByText('$3 할인 쿠폰')).toBeInTheDocument();
expect(await screen.findByText('$502.00')).toBeInTheDocument();
});
it('percent 타입의 쿠폰인 경우, 총 결제 금액은 "$455.00"로 노출된다', async () => {
await render(<TestPayment coupon={3} />);
expect(await screen.findByText('10% 할인 쿠폰')).toBeInTheDocument();
expect(await screen.findByText('$455.00')).toBeInTheDocument();
});
3. ShippingInformationForm 컴포넌트 통합 테스트
import { screen } from '@testing-library/react';
import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { NO_COUPON_ID } from '@/constants';
import ShippingInformationForm from '@/pages/purchase/components/ShippingInformationForm';
import render from '@/utils/test/render';
const TestForm = props => {
const methods = useForm({
defaultValues: {
name: '',
address: '',
phone: '',
requests: '',
coupon: NO_COUPON_ID,
...props,
},
});
return (
<FormProvider {...methods}>
<ShippingInformationForm />
<button type="button" onClick={methods.handleSubmit(() => {})}>
테스트 버튼
</button>
</FormProvider>
);
};
it('쿠폰 데이터를 가져오면 정상적으로 쿠폰 항목을 노출한다.', async () => {
const { user } = await render(<TestForm />);
const [selectBoxButton] = await screen.findAllByRole('button');
await user.click(selectBoxButton);
expect(screen.getByText('가입 기념! $5 할인 쿠폰')).toBeInTheDocument();
expect(screen.getByText('$3 할인 쿠폰')).toBeInTheDocument();
expect(screen.getByText('10% 할인 쿠폰')).toBeInTheDocument();
});
it('이름을 입력하지 않고 폼 전송을 시도하면 "이름을 입력하세요" 텍스트가 노출된다.', async () => {
const { user } = await render(<TestForm />);
const testSubmitButton = await screen.findByText('테스트 버튼');
await user.click(testSubmitButton);
expect(screen.getByText('이름을 입력하세요')).toBeInTheDocument();
});
it('주소를 입력하지 않고 폼 전송을 시도하면 "주소를 입력하세요" 텍스트가 노출된다.', async () => {
const { user } = await render(<TestForm />);
const testSubmitButton = await screen.findByText('테스트 버튼');
await user.click(testSubmitButton);
expect(screen.getByText('주소를 입력하세요')).toBeInTheDocument();
});
it('휴대폰 번호를 입력하지 않고 폼 전송을 시도하면 "휴대폰 번호를 입력하세요" 텍스트가 노출된다.', async () => {
const { user } = await render(<TestForm />);
const testSubmitButton = await screen.findByText('테스트 버튼');
await user.click(testSubmitButton);
expect(screen.getByText('휴대폰 번호를 입력하세요')).toBeInTheDocument();
});
it('휴대폰 번호의 패턴이 틀린 상태에서 폼 전송을 시도하면 "휴대폰 번호를 입력하세요" 텍스트가 노출된다.', async () => {
const { user } = await render(<TestForm phone="01099999999" />);
const testSubmitButton = await screen.findByText('테스트 버튼');
await user.click(testSubmitButton);
expect(
screen.getByText('-를 포함한 휴대폰 번호만 가능합니다'),
).toBeInTheDocument();
});
4. 베송 주문 페이지의 통합 테스트 한계
배송 주문 페이지에서 가장 중요한 테스트는
- 구매하기 버튼을 눌렀을 때, 올바른 주문서 데이터가 만들어 지는지
- 이후 결제 성공 또는 실패 시, 올바르게 처리되는지
하지만, 이 두 가지 테스트를 통합 테스트로 작성하면 많은 모킹이 필요하다.
각 컴포넌트의 스토어, API 모킹 뿐만 아니라 구매하기 버튼에 따른 성공, 다양한 실패 케이스에 따른 API 응답까지 모두 모킹해야 한다.
또한, 결제 방법에 따라 빌링 프로세스가 다르다면 이 또한 모두 모킹해야 한다.
이런 너무 많은 모킹은 빼먹는 시나리오가 생길 수 있고, 모킹에 의존적이므로 신뢰도가 떨어진다.
이를 위해 E2E 테스트가 필요하다.