
너무나 겁먹었던 테스트 코드 챕터... 간단히 기록해보자
import { render, screen } from '@testing-library/react';
test('should show login form', () => {
render(<Login />)
// 화면에서 Username 레이블 텍스트를 가진 요소 가져옴
const input = screen.getByLabelText('Username');
})
it('기존 일정의 세부 정보를 수정하고 변경사항이 정확히 반영된다', async () => {
setupMockHandlerUpdating();
const { user } = setup(<App />);
const editButtons = await screen.findAllByRole('button', { name: 'Edit event' });
const titleInput = screen.getByLabelText('제목');
await user.click(editButtons[0]); // 수정 버튼 클릭
expect(screen.getByRole('button', { name: '일정 수정' })).toBeInTheDocument();
expect(screen.getByDisplayValue('기존 회의')).toBeInTheDocument();
await user.clear(titleInput); // 입력 필드 값 초기화
await user.type(titleInput, '기존 회의3'); // 입력 필드에 '기존 회의3' 텍스트 입력
await user.click(screen.getByRole('button', { name: '일정 수정' })); // 일정 수정 버튼 클릭
const eventList = within(screen.getByTestId('event-list'));
expect(await eventList.findByText('기존 회의3')).toBeInTheDocument();
});
| 쿼리 유형 | 0개 일치 | 1개 일치 | 1개 이상 일치 | 재시도(비동기) |
|---|---|---|---|---|
getBy... | 오류 발생 | 요소 반환 | 오류 발생 | x |
queryBy... | null 반환 | 요소 반환 | 오류 발생 | x |
findBy... | 오류 발생 | 요소 반환 | 오류 발생 | o |
| 쿼리 유형 | 0개 일치 | 1개 일치 | 1개 이상 일치 | 재시도(비동기) |
|---|---|---|---|---|
getAllBy... | 오류 발생 | 배열 반환 | 배열 반환 | x |
queryAllBy... | [] 반환 | 배열 반환 | 배열 반환 | x |
findAllBy... | 오류 발생 | 배열 반환 | 배열 반환 | o |
test('', () => {
screen.getByRole('button');
});
test('', () => {
// data-testid="test-button" 속성을 가진 요소
screen.getByTestId('test-button');
});
// <div>Hello World</div>
screen.getByText('Hello World') // 텍스트가 매치되는 요소
screen.getByText('llo Worl', {exact: false}) // 일부만 매치
screen.getByText(/World/) // 정규식 활용
screen.getByText((content, element) => content.startsWith('Hello')) // 함수로 가져옴
+) 🤜 과제를 통해 알게된 점.. 🥸
// src/__mocks__/handlersUtils.ts
export const setupMockHandlerDeletion = () => {
const mockEvents: Event[] = [
{
id: '1',
title: '삭제할 이벤트',
date: '2025-10-15',
startTime: '09:00',
endTime: '10:00',
description: '삭제할 이벤트입니다',
location: '어딘가',
category: '기타',
repeat: { type: 'none', interval: 0 },
notificationTime: 10,
},
];
server.use(
http.get('/api/events', () => {
return HttpResponse.json({ events: mockEvents });
}),
http.delete('/api/events/:id', ({ params }) => {
const { id } = params;
const index = mockEvents.findIndex((event) => event.id === id);
mockEvents.splice(index, 1);
return new HttpResponse(null, { status: 204 });
})
);
};
// src/__tests__medium.integration.spec.tsx
it("네트워크 오류 시 '일정 삭제 실패'라는 텍스트가 노출되며 이벤트 삭제가 실패해야 한다", async () => {
setupMockHandlerDeletion();
// 삭제 처리가 실패하도록 덮어씀
server.use(
http.delete('/api/events/:id', () => {
return HttpResponse.error();
})
);
const { result } = renderHook(() => useEventOperations(true));
await act(async () => {
result.current.deleteEvent('1');
});
expect(enqueueSnackbarFn).toHaveBeenCalledWith('일정 삭제 실패', { variant: 'error' });
});
우선 각 테스트에서 사용되는 목 데이터와 서버 처리 로직을 하나의 함수로 묶어서 모킹한 부분이 되게 인상적이었다... 반복되는 목 데이터 재사용도 가능하고 엄청 깔끔해보였다. 하나의 함수를 여러 개의 테스트에 적용할 수 있으니까!!
그리고 처음 네트워크 오류 처리를 다루는 테스트를 접하고 도대체 어떻게 작성해야 하는건지 상상도 되지 않았다... 이러한 특수한 상황도 테스트가 가능하다는 사실을 처음 알게 되었다. 실제 환경에서 발생할 수 있는 여러 케이스를 시뮬레이션하고, ui 반응이나 오류 처리 로직을 검증할 수 있다는게 놀라웠다. 👍
꾸준히... 과제를 하자
역시 경험이 많이 없다보니 너무 생소하게 느껴졌다... 앞으로 테스트 코드와 더 친해져보자..
개인 플젝에 테스트 코드 도입해보기