본문 바로가기

카테고리 없음

[React] React로 할 일 관리 앱 만들기: 9장 - 단위 테스트 작성하기

반응형

 

안녕하세요! 이번 포스트에서는 우리가 만든 할 일 관리 앱에 대한 단위 테스트를 작성하는 방법을 알아보겠습니다. Jest와 React Testing Library를 사용하여 컴포넌트, Redux 액션, 리듀서에 대한 테스트를 작성해 보겠습니다.

1. 테스트 환경 설정

Create React App으로 프로젝트를 만들었다면 Jest와 React Testing Library가 이미 설정되어 있습니다. 추가로 필요한 패키지들을 설치해 보겠습니다:

npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event

2. 컴포넌트 테스트 작성하기

먼저 TodoForm 컴포넌트에 대한 테스트를 작성해 보겠습니다. src/components 폴더에 TodoForm.test.js 파일을 생성합니다:

// src/components/TodoForm.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import TodoForm from './TodoForm';
import { addTodoAsync } from '../store/todoSlice';

const mockStore = configureStore([]);

describe('TodoForm', () => {
  let store;

  beforeEach(() => {
    store = mockStore({});
    store.dispatch = jest.fn();
  });

  test('renders input and submit button', () => {
    render(
      <Provider store={store}>
        <TodoForm />
      </Provider>
    );

    expect(screen.getByPlaceholderText('새로운 할 일을 입력하세요')).toBeInTheDocument();
    expect(screen.getByText('추가')).toBeInTheDocument();
  });

  test('dispatches addTodoAsync action when form is submitted', () => {
    render(
      <Provider store={store}>
        <TodoForm />
      </Provider>
    );

    const input = screen.getByPlaceholderText('새로운 할 일을 입력하세요');
    const submitButton = screen.getByText('추가');

    fireEvent.change(input, { target: { value: '새로운 할 일' } });
    fireEvent.click(submitButton);

    expect(store.dispatch).toHaveBeenCalledWith(addTodoAsync('새로운 할 일'));
  });
});

3. Redux 액션 및 리듀서 테스트 작성하기

이제 Redux 액션과 리듀서에 대한 테스트를 작성해 보겠습니다. src/store 폴더에 todoSlice.test.js 파일을 생성합니다:

// src/store/todoSlice.test.js
import todoReducer, {
  addTodoAsync,
  toggleTodoAsync,
  deleteTodoAsync,
  editTodoAsync
} from './todoSlice';
import { configureStore } from '@reduxjs/toolkit';

describe('todoSlice', () => {
  let store;

  beforeEach(() => {
    store = configureStore({
      reducer: {
        todos: todoReducer
      }
    });
  });

  test('should handle initial state', () => {
    expect(store.getState().todos).toEqual({
      items: [],
      status: 'idle',
      error: null
    });
  });

  test('should handle addTodoAsync', async () => {
    await store.dispatch(addTodoAsync('New Todo'));
    const todos = store.getState().todos.items;
    expect(todos.length).toBe(1);
    expect(todos[0].text).toBe('New Todo');
  });

  test('should handle toggleTodoAsync', async () => {
    await store.dispatch(addTodoAsync('Test Todo'));
    const todoId = store.getState().todos.items[0].id;
    await store.dispatch(toggleTodoAsync(todoId));
    const toggledTodo = store.getState().todos.items[0];
    expect(toggledTodo.completed).toBe(true);
  });

  test('should handle deleteTodoAsync', async () => {
    await store.dispatch(addTodoAsync('Test Todo'));
    const todoId = store.getState().todos.items[0].id;
    await store.dispatch(deleteTodoAsync(todoId));
    expect(store.getState().todos.items.length).toBe(0);
  });

  test('should handle editTodoAsync', async () => {
    await store.dispatch(addTodoAsync('Test Todo'));
    const todoId = store.getState().todos.items[0].id;
    await store.dispatch(editTodoAsync({ id: todoId, text: 'Edited Todo' }));
    const editedTodo = store.getState().todos.items[0];
    expect(editedTodo.text).toBe('Edited Todo');
  });
});

4. 비동기 API 호출 테스트하기

비동기 API 호출을 테스트하기 위해 Jest의 mock 기능을 사용할 수 있습니다. src/api 폴더에 todoApi.test.js 파일을 생성합니다:

// src/api/todoApi.test.js
import {
  fetchTodos,
  addTodoApi,
  toggleTodoApi,
  deleteTodoApi,
  editTodoApi
} from './todoApi';

// API 함수들을 모의(mock)합니다
jest.mock('./todoApi');

describe('Todo API', () => {
  test('fetchTodos should return todos', async () => {
    const mockTodos = [{ id: 1, text: 'Test Todo', completed: false }];
    fetchTodos.mockResolvedValue(mockTodos);

    const todos = await fetchTodos();
    expect(todos).toEqual(mockTodos);
  });

  test('addTodoApi should add a new todo', async () => {
    const newTodo = { id: 2, text: 'New Todo', completed: false };
    addTodoApi.mockResolvedValue(newTodo);

    const result = await addTodoApi('New Todo');
    expect(result).toEqual(newTodo);
  });

  test('toggleTodoApi should toggle todo completion status', async () => {
    const toggledTodo = { id: 1, text: 'Test Todo', completed: true };
    toggleTodoApi.mockResolvedValue(toggledTodo);

    const result = await toggleTodoApi(1);
    expect(result).toEqual(toggledTodo);
  });

  test('deleteTodoApi should delete a todo', async () => {
    deleteTodoApi.mockResolvedValue(1);

    const result = await deleteTodoApi(1);
    expect(result).toBe(1);
  });

  test('editTodoApi should edit a todo', async () => {
    const editedTodo = { id: 1, text: 'Edited Todo', completed: false };
    editTodoApi.mockResolvedValue(editedTodo);

    const result = await editTodoApi(1, 'Edited Todo');
    expect(result).toEqual(editedTodo);
  });
});

5. 테스트 실행하기

모든 테스트를 실행하려면 터미널에서 다음 명령어를 실행하세요:

npm test

특정 테스트 파일만 실행하려면 파일 이름을 지정할 수 있습니다:

npm test TodoForm.test.js

마무리

이번 챕터에서는 React 컴포넌트, Redux 액션 및 리듀서, 그리고 비동기 API 호출에 대한 단위 테스트를 작성하는 방법을 배웠습니다. 테스트를 작성함으로써 얻을 수 있는 장점은 다음과 같습니다:

  1. 버그 조기 발견: 코드 변경 시 발생할 수 있는 버그를 빠르게 찾아낼 수 있습니다.
  2. 리팩토링 용이성: 기존 기능이 올바르게 동작하는지 확인하며 코드를 개선할 수 있습니다.
  3. 문서화: 테스트는 코드의 동작 방식을 설명하는 또 다른 형태의 문서가 됩니다.
  4. 개발 속도 향상: 장기적으로 버그 수정 시간을 줄이고 개발 속도를 높일 수 있습니다.

테스트 작성에 대해 어떻게 생각하시나요? 테스트 작성이 개발 과정에 도움이 된다고 느끼시나요? 여러분의 경험과 생각을 댓글로 공유해 주세요!

다음 챕터에서는 이 할 일 관리 앱을 배포하는 방법에 대해 알아보겠습니다. GitHub Pages나 Netlify 같은 플랫폼을 사용하여 우리의 앱을 온라인에 공개하는 과정을 다룰 예정입니다. React 앱 개발과 테스트에 대해 궁금한 점이 있다면 언제든 질문해 주세요!

반응형