TDD(Test Drive Development)-테스트 주도 개발 ?
실제 코드를 작성하기 전에 테스트 코드를 먼저 작성, 테스트 코드를 통화할 수 있는 실제 코드를 작성하는 개발 방식
일반적인 개발자의 뇌구조(사실 내 뇌구조)로 본 개발과정(비효율적일 수 있음)
- 어떤 기능을 구현할 지 생각한다(머리로 구상해봄)
- 실제 기능 구현 시작
- 되나 안되나 테스트 해봄(실행시켜 봄)
- 되면 좋고
- 안되면 우울함.. 될 때까지 해봄
테스트 주도 개발 방식으로 개발하는 과정
- 어떤 기능을 구현할 지 생각
- 해당 기능이 통과해야 할 최소한의 조건을 테스트 코드로 만든다
- 조건에 맞춰가며 구현을 시작
- 충족하지 못하는 조건이 없을 때까지 한다!
Jest 주요 API
- describe: 여러 종류의 테스트를 그룹화하는 블록(= 테스트를 모아둔 파일 한개)
- it: 개별 테스트를 수행하는 블록(=1개의 테스트)
ex. test('테스트 이름', () => { 테스트 내용 })
- expect: 값을 테스트하는 메소드, matcher와 함께 사용
ex. expect(linkElement).toBeInTheDocument();
//기대한다(-에).matcher() 무엇을?
- 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 />);
const linkElement = screen.getByText("This is TDD");
expect(linkElement).toBeInTheDocument();
});
- render: dom이 형성되는 절차
- screen: 결과화면
테스트 실행 명령어
npm run test
실습 2) 조금 긴 테스트
import { useState } from "react";
function App() {
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>
{}
<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>
{}
<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);
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 문 이후 화면에 실제로 렌더링되고 표시되는 내용을 테스트하는 데 중점을 두고 있음
- 컴포넌트의 내부 구현 세부 사항에만 초점을 두는 대신 사용자의 관점에서 테스트하는 철학과 일치
- 큰 프로젝트에서 일정 기간마다 개발 후 배포가 이루어 지는 프로그램에서 적당할 것 같다는 느낌이 들었음