테스트 주도 개발 방식으로 컴포넌트를 만들어보자-! feat) jest

Hyodduru ·2022년 10월 27일
3

React

목록 보기
22/22

지금껏 나는 그냥 무작정 기능들이 필요할 때마다 별다른 고민없이 컴포넌트를 무작정 만들어왔던 것 같다. 흑,,

괜차나 이제부터라도 제대로 고민해도... 늦지않아...(?)

아무튼,,,

내가 "왜" 이 컴포넌트를 만드는지, 이 컴포넌트의 목적이 무엇인지 고민없이 만들면, 개발 속도 자체는 빠를지 몰라도, 재사용하는 데에 어려움이 있고 관련 로직이 분산될 수 있다는 점에서 좋지 않은 것 같다.

누가 봐도 맥락이 이해가 가게끔, 해당 컴포넌트가 최대한 단독적으로 사용할 수 있어서 어디서든 재사용할 수 있게끔 만들어주어, 그 존재 목적을 확실하게 해주는 것이 좋은 코드를 만드는 데 중요하다는 생각이 든다.

그 때 좋은 것중 하나가 "테스트 개발 주도 방식"인데, 컴포넌트를 만들기 전에
해당 컴포넌트 관련 테스트 시나리오를 만드는 것이다.

시나리오 패턴은 아래와 같다.

given (준비) - when(실행) - then (검증)

참고자료 : https://cucumber.io/docs/cucumber/configuration/?lang=java
https://martinfowler.com/bliki/GivenWhenThen.html

📝 테스트코드 작성을 위한 시나리오

상황을 가정해서 컴포넌트를 만든다고 해보자.

나는 로딩이 이루어지는 시간동안 화면에 로딩 화면을 보여주는 컴포넌트를 만들고 싶다.
로딩중에는 로딩화면을 보여주고,
로딩이 완료가 되었을 때는 로딩이 완료되는 시점에 메인 화면으로 넘어가게끔 하고 싶다.

위의 상황에 대한 시나리오를 짜면 다음과 같다.

given - 로딩이 완료가 되었다.
when -
then - 로딩을 종료시킨다.

given - 로딩이 완료가 되지 않았다.
when -
then - 로딩 화면을 보여준다.

(로딩의 여부에 따라서만 결과 값이 달라져서 when에는 무언갈 넣기 어려워서 생략함)

로딩의 완료여부에 따라 두 시나리오로 구성이 된다.

로딩화면 컴포넌트는 로딩을 하는 중 보여지는 컴포넌트라는 목적을 갖고 있기 때문에 위의 두 시나리오로 생각해볼 수 있었다.

🧪 시나리오를 바탕으로 테스트코드 작성하기

위의 시나리오를 통해 테스트 코드를 작성하면 아래와 같다.

import Loading from './Loading';

import { render, screen, waitFor } from 'testing-utils';

describe('로딩 컴포넌트 테스트', () => {
  const onLoadingEnd = jest.fn();

  describe('로딩 상태인 경우', () => {
    beforeEach(() => {
      render(
        <Loading loading={true} onLoadingEnd={onLoadingEnd} />,
        {}
      );
    });

    it('로딩 이미지가 노출되어야 한다.', async () => {
      await waitFor(() =>
        expect(screen.getByTestId('loading-image')).toBeInTheDocument()
      );
    });
  });

  describe('로딩이 끝난 경우', () => {
    beforeEach(() => {
      render(
        <Loading loading={false} LoadingEnd={onLoadingEnd} />,
        {}
      );
    });

    it('로딩이 종료되어야 한다.', async () => {
      await waitFor(() => expect(onLoadingEnd).toBeCalled());
    });
  });
});

🧽 직접 사용할 컴포넌트 만들기

테스트 코드를 작성하고나면 위 시나리오를 바탕으로 컴포넌트를 직접 만들어볼 수 있다!

간단하게만 만들어보면,

import { useEffect, useState } from 'react';
import { ReactComponent as LoadingImg } from 'assets/images/loading-1.svg';

type LoadingProps = {
  loading: boolean;
  onLoadingEnd: () => void; // loading state를 false로 만들어주는 함수. 즉 로딩을 멈춰준다. 
};

const Loading = ({
  loading,
  onLoadingEnd,
}: LoadingProps) => {

  useEffect(() => {
    if (!loading) {
      onLoadingEnd();
    }
  }, [loading, onLoadingEnd]);

  return (
    <div>
      <LoadingImg1 data-testid="loading-image" />
    </div>
  );
};

export default Loading;

위의 로딩 컴포넌트 활용법은 아래 조건식으로 사용해줄 수 있다.

const Main = () => {
  const [loading, setLoading] = useState(false);
  
  return (
  	// ...
    {loading?  <Loading loading = {loading} onLoadingEnd={()=> setLoading(false)} /> 
  : <div> something... main </div>}
  )
}

이렇게 하면 기존의 목적에 맞게 로딩 중일 때만 해당 컴포넌트를 보여줄 수 있는 기능을 수행하게 되고, 어디에든 쉽게 사용할 수 있어서 재사용성이 생긴다!

🍯 이 글을 마치며

아직도 배워가는 단계로 갈길이 멀지만, 계속해서 시나리오를 구상해보고, 내가 하는 이 개발의 목적은 무엇일까, 여기서의 요구사항은 무엇일까 늘 고민해야겠다는 생각을 한다.
화이팅 👊 👊 👊 👊 👊

profile
꾸준히 성장하기🦋 https://hyodduru.tistory.com/ 로 블로그 옮겼습니다

0개의 댓글