2차 공부/TIL

24.08.21 TDD체험해보기

공대탈출 2024. 8. 21. 23:13

+버튼과 -버튼이 있고, count를 화면에 보여주는 컴포넌트가 있다고 생각해보자.

컴포넌트를 만들고 작동시키기 전에 TDD는 테스트코드부터 작성해야한다.

import { render, screen } from "@testing-library/react";
import App from "./App";

test("the counter starts at 0", () => {
    render(<App />);
    //screen object를 이용해서 원하는 엘리먼트에 접근(ID)
    const counterElement = screen.getByTestId("counter");
    //id가 counter인 엘리먼트의 텍스트가 0인지 테스트
    expect(counterElement).toBe(0);
});

counter을 id로 갖는 요소에 접근하여 텍스트가 0일 것이라고 예상하는 테스트이다.

당연하게도 에러가 발생한다. counter이라는 id를 가진 요소가 없기 때문이다.

TDD의 과정중 해당 부분에 위치한 것이다.

따라서 이제는 테스트코드에 알맞게 counter라는 id를 가진 요소를 만들고, 해당 요소가 0이라는 count값을 Text로 가지게 만들어야한다.

 

이렇게 바꿔주면 에러가 발생하지 않을까?

그렇다고 생각하겠지만, 에러가 발생한다. 그 이유는 테스트를 위한 id값을 넣어야 하기 때문이다.

에러 메시지에 보면, data-testid가 counter인 요소를 찾지 못한다고 알려주고 있다.

하지만 또 에러가 발생한다. 우리가 testid가 counter인 요소를 선택했지만, 현재 코드로는

<h3 data-testid='counter'>0</h3> === 0을 비교하려고 하다보니 생긴 오류이다. 따라서 해당 요소의 textContent를 가져와야 한다.

이렇게 내부 텍스트콘텐트를 가져와서 toBe로 비교해도 되고, matcher을 toHaveTextContent로 사용해도 된다.

 

테스트는 통과되었으나 이런 오류가 뜰 수 있다. 이건 testing-library의 버전이 오래된 것이 CRA를 통해 설치되어 있기 때문이라 다시 install해주면 된다.

npm install --save-dev @testing-library/react

깔끔한 테스트 통과

 

여기서 주의해야 할 점은 element.textContent로 불러온 값은 문자열로 return해주기 때문에, 아무리 count가 숫자형태의 state일지라도 문자열로 들어와 toBe(0)의 숫자와 타입이 달라 테스트 Fail을 받을 수 있다.


이제 counter을 변화시킬 +버튼과 -버튼에 대한 테스트도 진행해보자.

test("minus button has correct text", () => {
    render(<App />);
    const minusButtonElement = screen.getByTestId("minusButton");
    expect(minusButtonElement).toHaveTextContent("-");
});

test("plus button has correct text", () => {
    render(<App />);
    const plusButtonElement = screen.getByTestId("plusButton");
    expect(plusButtonElement).toHaveTextContent("+");
});

위와 동일하게 각 버튼요소에 접근하고, 해당 버튼이 알맞은 Text를 가지고 있는지 검사해주는 테스트를 작성했다.

당연하게도 오류가 난다. 왜? TDD의 첫 단계이니까.

이제 테스트 코드에 맞는 실제 코드를 작성해주면 된다.

import React, { useState } from "react";

const App = () => {
    const [counter, setCounter] = useState(0);

    return (
        <div>
            <h3 data-testid="counter">{counter}</h3>
            <div>
                <button data-testid="minusButton">-</button>
                <button data-testid="plusButton">+</button>
            </div>
        </div>
    );
};

export default App;

세가지 테스트 모두 정상적으로 통과가 된 모습이다.


이제 버튼의 기능에 대한 테스트를 해야한다. 기능에 관한 테스트는 fire event문서를 읽어보면 된다.

 

test("when the + button is pressed, the counter changes to 1", () => {
    render(<App />);

    const plusButtonElement = screen.getByTestId("plusButton");
    //counter을 id로 가진 버튼에 클릭이벤트를 작동한 상태
    fireEvent.click(plusButtonElement);

    const counterElement = screen.getByTestId("counter");
    //클릭 이벤트 후에 counterElement가 1을 가지고 있는지 확인
    expect(counterElement).toHaveTextContent(1);
});

fireEvent의 click을 사용하여 버튼을 클릭한 상태를 테스트한다. 클릭을 진행하면 1을 더해야하기 때문에 counterElement가 1을 Text로 가지고있는지 확인하는 테스트이다.

당연히 실패, 이제 테스트코드에 대응하는 실제 코드를 작성한다.

import React, { useState } from "react";

const App = () => {
    const [counter, setCounter] = useState(0);

    return (
        <div>
            <h3 data-testid="counter">{counter}</h3>
            <div>
                <button data-testid="minusButton">-</button>
                <button data-testid="plusButton" onClick={() => setCounter((count) => count + 1)}>
                    +
                </button>
            </div>
        </div>
    );
};

export default App;

테스트가 잘 통과된 것을 볼 수 있다. 동일하게 마이너스 버튼도 작업해준다.

test("when the - button is pressed, the counter changes to -1", () => {
    render(<App />);

    const minusButtonElement = screen.getByTestId("minusButton");
    //counter을 id로 가진 버튼에 클릭이벤트를 작동한 상태
    fireEvent.click(minusButtonElement);

    const counterElement = screen.getByTestId("counter");
    //클릭 이벤트 후에 counterElement가 -1을 가지고 있는지 확인
    expect(counterElement).toHaveTextContent(-1);
});