210827 TIL TDD로 컴포넌트 만들기

» 2막, TIL (Today I Learned), TDD, 마이그레이션

TDD로 컴포넌트 만들기

이번 마이그레이션은 최대한 TDD로 진행해보려고 하고 있다. 전체 페이지를 TDD로 하자!고 생각했더니 너무 사이즈가 커서 벅찬 느낌이라, 마이페이지의 가장 작은 단위인 카드 정보 컴포넌트를 TDD로 만들어보았다.

단계

image 위 이미지를 그리는 컴포넌트를 만들어보려고 한다.

1. 초기 렌더 test를 짠다.

image

2. 파일이 없어서 에러가 나는 것을 확인한다.

image

3. 컴포넌트를 만든다.

image

4. 달라진 오류 내용을 확인한다.

image

5. red를 green으로 바꿔주는 코드를 작성한다.

image

6. ta-da! test를 통과한다.

image

이런식으로 단위를 쪼개서 작게 해보니 더 큰 그림이 그려지고 만족도도 높다.
테스트 짱!

라고 써놓았지만 또 막상 계속 진행해보면 머리가 아파온다..

그래서 아래처럼 카드 정보가 있을때, 없을떄로 테스트를 짰고

import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import MypageCard, { CardInfoProps } from 'src/components/mypage/MypageCard';

describe('MypageCard', () => {
  const renderMypageCard = ({ cardName, cardNumber, onClick }: CardInfoProps) => render((
    <MypageCard
      cardName={cardName}
      cardNumber={cardNumber}
      onClick={onClick}
    />
  ));
  const onClickMock = jest.fn();
  beforeEach(() => {
    onClickMock.mockClear();
  });

  const mockCardInfo = {
    cardName: 'BC카드',
    cardNumber: '************9566',
    onClick: onClickMock,
  };

  it('renders component', () => {
    const { container } = renderMypageCard(mockCardInfo);
    const menuTitle = '현재 등록된 카드';
    expect(container).toHaveTextContent(menuTitle);
  });

  context('when cardInfo is null', () => {
    it('returns \'카드 정보가 없습니다.\'', () => {
      const { container } = renderMypageCard({ onClick: onClickMock });
      const noCardMsg = '현재 등록된 카드가 없습니다. 카드 정보를 등록해주세요.';
      const noCardSubMsg = '카드를 등록하면 현재 이용중인 수강권의 수업이 모두 진행되었을 때 해당 카드로 다음 과외가 자동 결제됩니다.';
      expect(container).toHaveTextContent(noCardMsg);
      expect(container).toHaveTextContent(noCardSubMsg);
    });

    it('returns \'카드 등록하기\' button', () => {
      const { container } = renderMypageCard({ onClick: onClickMock });
      const btnText = '카드 등록하기';
      expect(container).toHaveTextContent(btnText);
    });

    it('triggers onClick event when the button is clicked', () => {
      const { getByText } = renderMypageCard(mockCardInfo);
      const btnText = '카드 등록하기';
      const btn = getByText(btnText);
      fireEvent.click(btn);
      expect(onClickMock).toHaveBeenCalledTimes(1);
    });
  });

  context('when cardInfo is exist', () => {
    it('returns card info', () => {
      const { container } = renderMypageCard(mockCardInfo);
      expect(container).toHaveTextContent(mockCardInfo.cardName);

      const cardLastFourNumbers = mockCardInfo.cardNumber.split('').slice(12, 16).join('');
      expect(container).toHaveTextContent(cardLastFourNumbers);
    });

    it('returns \'카드 변경하기\' button', () => {
      const { container } = renderMypageCard(mockCardInfo);
      const btnText = '카드 변경하기';
      expect(container).toHaveTextContent(btnText);
    });

    it('triggers onClick event when the button is clicked', () => {
      const { getByText } = renderMypageCard(mockCardInfo);
      const btnText = '카드 변경하기';
      const btn = getByText(btnText);
      fireEvent.click(btn);
      expect(onClickMock).toHaveBeenCalledTimes(1);
    });
  });
});

7개의 실패하는 테스트가 완성되었다. image

컴포넌트로 돌아와 하나씩 통과시키는 중… image

계속 하나가 통과가 안되는 중.. image

🎉 성공 (중간에 한글로 테스트명 바꿈)

image

와 테스트의 중요성을 느꼈다. prop을 잘못 넘겨줘서 그런 거였음..
지금까지 해본 TDD의 장점은 코드를 짜다보면 어딘가에서 막혀서 계속 헤매다가 내가 뭘 하려고 했지? 놓칠 수가 있는데,
미리 테스트를 짜고 들어가니 그런 부분을 안 놓치게 되는 점이 좋았다.

하지만 시간이 무지막지 오래 걸리는게 너무나 큰 단점…. 물론 멀리 보면 오히려 시간을 적게 쓰는거라고는 하지만…
매일 매일 빨리 빨리 생산해 내야하는 스타트업에선 어렵지 않을까.. 나조차도 아직까지 혼자 하고 있었으면 엄두도 못 냈을듯 하다.