TDD(Test Drive Development)-테스트 주도 개발 훑고 지나가요 !

const job = '프론트엔드';·2023년 8월 14일
0
post-thumbnail

TDD(Test Drive Development)-테스트 주도 개발 ?

실제 코드를 작성하기 전에 테스트 코드를 먼저 작성, 테스트 코드를 통화할 수 있는 실제 코드를 작성하는 개발 방식

일반적인 개발자의 뇌구조(사실 내 뇌구조)로 본 개발과정(비효율적일 수 있음)

  1. 어떤 기능을 구현할 지 생각한다(머리로 구상해봄)
  2. 실제 기능 구현 시작
  3. 되나 안되나 테스트 해봄(실행시켜 봄)
  4. 되면 좋고
  5. 안되면 우울함.. 될 때까지 해봄

테스트 주도 개발 방식으로 개발하는 과정

  1. 어떤 기능을 구현할 지 생각
  2. 해당 기능이 통과해야 할 최소한의 조건을 테스트 코드로 만든다
  3. 조건에 맞춰가며 구현을 시작
  4. 충족하지 못하는 조건이 없을 때까지 한다!

Jest 주요 API

  1. describe: 여러 종류의 테스트를 그룹화하는 블록(= 테스트를 모아둔 파일 한개)
  2. it: 개별 테스트를 수행하는 블록(=1개의 테스트)
    ex. test('테스트 이름', () => { 테스트 내용 })
  3. expect: 값을 테스트하는 메소드, matcher와 함께 사용
ex.  expect(linkElement).toBeInTheDocument();
     //기대한다(-에).matcher() 무엇을?
  1. matcher: 값의 테스트 방법을 정의

TDD 방법 1) React Testing Library

  • DOM 노드를 테스트 하고자 할 때, DOM Testing Library 솔루션 사용 가능
  • React일때 관련 기능을 추가하고 구축한 React Testing Library를 이용해 리액트 컴포넌트에 대한 테스트 수행 가능
  • CRA를 통해 생성한 프로젝트에는 기본적으로 React Testing Library가 포함되어 있음
  • 별도 install이 필요 없지만, 손수 프로젝트를 생성했다면?
npm install --save-dev @testing-library/react 

TDD 방법 2) Jest

  • 페이스북에서 만든 테스팅 프레임워크
  • 적은 양의 코드만으로도 단위별 테스트를 수행할 수 있음
  • CRA를 통해 생성한 프로젝트에는 기본적으로 포함되어 있음
  • 별도 install이 필요 없지만, 손수 프로젝트를 생성했다면?
npm install --save-dev jest 

실습 1)

import React from "react";

const App = () => {
  return <p>This is TDD</p>;
};

export default App;
  • 이런 리액트 컴포넌트 코드를 실행하면
  • 화면에는 This is TDD라는 P태그 형식의 문자가 보여짐
  • 그러면 이 코드를 테스트 해보자 !

.test.js 확장자 파일 생성

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


test("renders learn react link", () => {
  render(<App />); // App컴포넌트 렌더링 해보기로 함
  const linkElement = screen.getByText("This is TDD"); //화면에서 This is TDD를 가져와보자
  expect(linkElement).toBeInTheDocument();
  //기대한다(-에).matcher()무엇을?
});

  • render: dom이 형성되는 절차
  • screen: 결과화면

테스트 실행 명령어

npm run test

  • 터미널에 테스트 결과가 보여짐

실습 2) 조금 긴 테스트

import { useState } from "react";

function App() {
  //상태관리 2개
  const [counter, setCounter] = useState(0);
  const [disabled, setDisabled] = useState(false);

  return (
    <div className="App">
      <header className="App-header">
        {/* 숫자표시 */}
       
        <h3 data-testid="counter">{counter}</h3>

        <div>
          {/* 버튼2개 있음 마이너스 버튼, 플러스 버튼 */}
          <button
            data-testid="minus-button"
            onClick={() => setCounter((count) => count - 1)}
            disabled={disabled}
          >
            -
          </button>
          <button
            data-testid="plus-button"
            onClick={() => setCounter((count) => count + 1)}
            disabled={disabled}
          >
            +
          </button>
        </div>
        <div>
          {/* 추가적인 버튼이 또 있음 disabled버튼 버튼을 누르면 true,false의 반전, 플마 버튼에 영향을 미침 */}
          <button
            data-testid="on/off-button"
            style={{ backgroundColor: "blue" }}
            onClick={() => setDisabled((prev) => !prev)}
          >
            on/off
          </button>
        </div>
      </header>
    </div>
  );
}

export default App;
  • 데이터 셋(data-testId)? testId 속성은 데이터 셋을 이용해 만들도록 되어있음
  • html 속성에서 존재하지 않는 것을 만들어서 자바스크립트로 처리하고 싶을 때 사용
  • 따라서, testid라는 이름의 데이터 셋 속성을 만들어서, 자바스크립틀가 읽을 수 있도록 한 것

첫번째 상태 테스트

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

두번째 증가, 감소 버튼에 각각 올바른 텍스트가 표기되어 있는지 테스트

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

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

세번째 증가/감소 버튼에서 클릭이벤트가 발생했을때, 증감이 정상적으로 이루어지는지 테스트

test("When the + button is pressed, the counter changes to 1", () => {
  render(<App />);
  const buttonElement = screen.getByTestId("plus-button");
  fireEvent.click(buttonElement); //fireEvent 객체가 클릭 메소드에 요소를 전달받아 클릭 이벤트를 시험해봄
  const counterElement = screen.getByTestId("counter");
  expect(counterElement).toHaveTextContent(1);
});

test("When the - button is pressed, the counter changes to -1", () => {
  render(<App />);
  const buttonElement = screen.getByTestId("minus-button");
  fireEvent.click(buttonElement);
  const counterElement = screen.getByTestId("counter");
  expect(counterElement).toHaveTextContent(-1);
});

feat. fireEvent?

  • 해당요소에 이벤트를 강제해봄: JavaScript를 사용하여 특정 요소에 대해 이벤트를 인위적으로 발생시키는 함수
  • 버튼을 실제로 클릭하지 않아도 버튼을 클릭한 것처럼 동작하게 할 수 있음

네번째 스타일이 올바르게 적용되어 있는지 테스트

test("on/off button has blue color", () => {
  render(<App />);
  const buttonElement = screen.getByTestId("on/off-button");
  expect(buttonElement).toHaveStyle({ backgroundColor: "tomato" });
});

다섯번째 버튼을 클릭했을때, 증감버튼이 disabled상태가 반전되는지 테스트

test("Prevent the -,+ button from being pressed when the on/off button is cliecked", () => {
  render(<App />);
  const onOffButtonElement = screen.getByTestId("on/off-button");
  fireEvent.click(onOffButtonElement);
  const plusButtonElement = screen.getByTestId("plus-button");
  expect(plusButtonElement).toBeDisabled();
});

테스트 결과

실패한 테스트 결과가 있을때(style부분 다르게 입력함),

  • 테스트에 통과하지 못했을 때, 어떤 부분 때문에 테스트에 통과하지 못했는지 터미널에서 보여줌

결론

TDD 테스트는

  • 컴포넌트의 return 문 이후 화면에 실제로 렌더링되고 표시되는 내용을 테스트하는 데 중점을 두고 있음
  • 컴포넌트의 내부 구현 세부 사항에만 초점을 두는 대신 사용자의 관점에서 테스트하는 철학과 일치
  • 큰 프로젝트에서 일정 기간마다 개발 후 배포가 이루어 지는 프로그램에서 적당할 것 같다는 느낌이 들었음
profile
`나는 ${job} 개발자`

0개의 댓글