본문 바로가기

Frontend

[FEConf Korea] 프론트엔드에서 TDD가 가능하다는 것을 보여드립니다.

이강의는 FEConf Korea 강의를 보고 정리한 자료입니다.

 

TDD. Test Driven Development

궁극적 목표는 Clean code that works. 작동하는 깔끔한 코드이다.

 

TDD

 

Testable 한 code를 어떻게 작성하는가?

Seperation of Concerns, 관심사의 분리를 통해서 testable한 코드를 작성합니다. 이 관심사의 분리를 지키지 않으면 거대한 진흙덩어리 코드가 만들어진다.

 

 

TDD 예제1

 

Redux -> 리액트의 관심사 때문에 쓴다. 리액트의 관심사는 state reflection, 상태의 반영이다. 상태의관리는 신경쓰지 않음.

컴포넌트에 상태가 전달이 된다면 화면을 리렌더링해야한다. 그렇기 때문에 리액트는 상태의 관리에 대해서는 관심이 없다는점

SRP (Single Responsibility Principle) 단일 책임 원칙을 지키면 TDD를 잘 지킬 수 있다.

아래는 리덕스의 Reducer 테스트 코드 예제입니다.

 

import reducer from './reducer';

import {
  setTasks,
  deleteTask,
} from './actions';

import tasks from '../fixtures/tasks';

describe('reducer', () => {
  describe('setTasks', () => {
    it('changes tasks array', () => {
      const initialState = {
        tasks: [],
      };

      const state = reducer(initialState, setTasks(tasks));

      expect(state.tasks).not.toHaveLength(0);
    });
  });

  describe('deleteTask', () => {
    context('with existed task ID', () => {
      it('remove the task from tasks', () => {
        const state = reducer({
          tasks: [
            { id: 1, title: 'Task' },
          ],
        }, deleteTask(1));

        expect(state.tasks).toHaveLength(0);
      });
    });

    context('without existed task ID', () => {
      it("doesn't work", () => {
        const state = reducer({
          tasks: [
            { id: 1, title: 'Task' },
          ],
        }, deleteTask(100));

        expect(state.tasks).toHaveLength(1);
      });
    });
  });
});

 

BDD는 무엇인가???

상황에 따라 다르게 행동하는 것을 테스트!!

 

describe('List', () => {
  const handleClickDelete = jest.fn();

  function renderList(tasks) {
    return render((
      <List
        tasks={tasks}
        onClickDelete={handleClickDelete}
      />
    ));
  }

  context('with tasks', () => {
    const tasks = [
      { id: 1, title: '할일 #1' },
      { id: 2, title: '할일 #2' },
    ];

    it('renders tasks', () => {
      const { getByText } = renderList(tasks);

      expect(getByText(/할일 #1/)).not.toBeNull();
      expect(getByText(/할일 #2/)).not.toBeNull();
    });

    it('renders "완료" button to delete a task', () => {
      const { getAllByText } = renderList(tasks);

      const buttons = getAllByText('완료');

      fireEvent.click(buttons[0]);

      expect(handleClickDelete).toBeCalledWith(1);
    });
  });

  context('without tasks', () => {
    it('render no tasks message', () => {
      const tasks = [];

      const { getByText } = renderList(tasks);

      expect(getByText(/할 일이 없어요/)).not.toBeNull();
    });
  });
});

 

TDD는 지뢰 탐지기이다.

지뢰탐지기가 있다고 모든 지뢰를 피할 수 없다. TDD를 위한 필요조건은 좋은 설계이다.
왜하는가? -> 똥이 클때와 똥이 작을 때 비교해보자. 덩어리가 작을 때 더 처리하기 쉽다.


미래의 고통을 지금으로 가져오는 기술. TDD.

TDD를 하게 되면 요구사항을 명확하게 할 수 있다. 유연한 도입이 필요하다.

 

연습할것:useSelector, useDispatch 를 mocking해서 테스트 코드 작성하기 (jest.fn()) 이용, 비동기 코드 테스트

코드 repo

'Frontend' 카테고리의 다른 글

React Query와 상태관리  (0) 2022.06.13
React Server Components  (0) 2022.06.09
em vs rem  (0) 2022.06.02
HTTP와 HTTPS  (0) 2022.03.06
[SLASH 21] 실무에서 바로쓰는 Frontend Clean Code  (0) 2022.02.24