테스트 주도 개발(Test-Driven Development, TDD)은 현대의 소프트웨어 개발 방법론 중 하나로 널리 알려져 있다. 이번 포스팅에서는 React를 사용한 간단한 예제를 통해 TDD의 기본 원리와 실제 적용 방법을 알아보겠다.
TDD는 테스트를 먼저 작성하고 이를 통과할 수 있는 코드를 구현하는 방식이다. 기본 원칙은 다음과 같다:
실패하는 테스트 작성: 원하는 기능의 요구 사항을 반영하는 테스트 코드를 먼저 작성한다.
테스트 통과: 테스트를 통과할 수 있는 최소한의 코드를 작성한다.
코드 개선(Refactor): 코드의 품질을 향상시킨다.
우리가 테스트할 예제는 간단한 Counter 컴포넌트와 이를 지원하는 useCounter Custom Hook이다. Counter 컴포넌트는 버튼을 통해 숫자를 증가시키거나 감소시키는 기능을 한다. useCounter는 해당 기능의 로직을 담당한다.
import { useState } from 'react'
type CounterHook = { count: number, decreaseCount: () => void, increaseCount: () => void }
export function useCounter(): CounterHook {
const [count, setCount] = useState(0)
const decreaseCount = () => setCount(count - 1)
const increaseCount = () => setCount(count + 1)
return { count, decreaseCount, increaseCount }
}
export default function Counter() {
const { count, decreaseCount, increaseCount } = useCounter()
return (
<div>
<h1 data-testid="count-value">{count}</h1>
<button data-testid="increase-button" onClick={increaseCount}>+</button>
<button data-testid="decrease-button" onClick={decreaseCount}>-</button>
</div>
)
}
우선, useCounter Custom Hook의 로직을 테스트한다. 여기서는 Hook의 초기 상태와, 특정 함수를 호출했을 때 상태가 제대로 변경되는지를 확인한다.
import { act, render, renderHook } from "@testing-library/react";
import Counter, { useCounter } from "./Counter";
import { GetByTestId } from "./auth/util";
describe("Counter 커스텀훅 테스트", () => {
let result: { current: ReturnType<typeof useCounter> };
beforeEach(() => {
result = renderHook(() => useCounter()).result;
});
it('카운트의 초기값이 0인지 확인', () => {
const countValue = result.current.count;
expect(countValue).toBe(0);
});
it('increaseCount 함수 호출 시 카운트가 1 증가하는지', () => {
act(() => {
result.current.increaseCount();
});
const countValue = result.current.count;
expect(countValue).toBe(1);
});
it('decreaseCount 함수 호출 시 카운트가 1 감소하는지', () => {
act(() => {
result.current.decreaseCount();
});
const countValue = result.current.count;
expect(countValue).toBe(-1);
});
});
다음으로, 컴포넌트가 올바르게 렌더링 되는지, 버튼을 클릭했을 때 상태가 변하는지를 테스트한다.
describe("Counter 컴포넌트 테스트", () => {
let getByTestId: GetByTestId;
beforeEach(() => {
const rendered = render(<Counter />);
getByTestId = rendered.getByTestId;
});
it('초기 카운트 값이 0인지 확인', () => {
const countValue = getByTestId('count-value');
expect(countValue).toHaveTextContent('0');
});
it('증가 버튼 클릭 시 카운트가 1 증가하는지', () => {
const increaseButton = getByTestId('increase-button');
act(() => {
// fireEvent.click(increaseButton);
increaseButton.click();
});
const countValue = getByTestId('count-value');
expect(countValue).toHaveTextContent('1');
});
it('감소 버튼 클릭 시 카운트가 1 감소하는지', () => {
const decreaseButton = getByTestId('decrease-button');
act(() => {
decreaseButton.click();
});
const countValue = getByTestId('count-value');
expect(countValue).toHaveTextContent('-1');
});
});
REFERENSE
좋은 글 감사합니다. 자주 방문할게요 :)