Test-driven Development
코드를 작성하기 전에 테스트를 쓰는 소프트웨어 개발 방법론
작은 단위의 테스트 케이스를 작성하고, 이를 통과하는 코드를 작성한다.
- Write Failing Test: 실패하는 테스트 코드를 먼저 작성한다.
- Make Test Pass: 테스트 코드를 성공시키기 위한 실제 코드를 작성한다.
- Refactor: 중복 코드 제거, 일반화 등의 리팩토링을 수행한다.
TDD를 따라 개발할 경우, 일반적으로 결함을 50~90%까지 감소시킬 수 있다.
불필요한 설계를 피할 수 있고, 요구 사항에 집중할 수 있게 된다.
버그를 줄여 코드를 작성하는 시간을 줄일 수 있게 된다.
console.log()
로 코드의 결과물을 확인하는 것도 일종의 테스트라고 할 수 있다.
코드에 대해 특정한 규칙(테스트)를 설정하기 위해 고민하고, 코드가 큰 틀에서 어떤 의미를 갖는지 고민하는 것에도 의의가 있다.
React에서 테스트는 Testing Library와 Jest를 이용해서 할 수 있다.
create-react-app으로 생성한 프로젝트는 npm run test
로 터미널에서 테스트를 진행할 수 있다. (자동으로 Testing library를 이용할 수 있다.)
Jest는 자바스크립트의 테스팅 프레임워크이자 test runner이다.
파일명.test.js
파일(또는 파일명.spec.js
파일)을 자동으로 찾아 테스트를 실행하고, 결과를 반환한다.
각각의 역할이 다르기 때문에 어느 한쪽만 가지고 테스트를 할 수 없다.
package.json에서 testing library를 확인할 수 있다.
@testing-library/jest-dom
: Jest-dom의 custom matcher를 사용할 수 있게 한다.
@testing-library/react
: 컴포넌트의 요소를 찾기 위한 query가 포함되어 있다.
@testing-library/user-event
: click 등 사용자 이벤트 테스트에 사용된다.
App.test.js에 작성되어 있는 테스트를 확인해보자
import { render, screen } from '@testing-library/react';
import App from './App';
//test , expect 함수: Jest의 함수
//toBeInTheDocument: jest-dom 라이브러리에 포함된 Custom Matchers
//jest-dom라이브러리는 setupTests.js에서 import 되고 있다.
test('renders learn react link', () => {
//test를 진행할 컴포넌트를 render
render(<App />);
//learn react라는 text가 있는지 확인
//getByText: screen의 methods 중 하나
//i: 대소문자 구분하지 않는다
const linkElement = screen.getByText(/learn react/i);
//learn react -> App.js 파일에 존재함. -> test pass
//지정한 요소가 document.body안에 존재하는지 체크
//toBeInTheDocument: matchers 함수
expect(linkElement).toBeInTheDocument();
});
아래와 같이 Jest만으로 간단한 테스트를 작성할 수도 있다.
//Example.test.js
//Test Suites
describe('간단한 테스트들', () => {
//test (test case)
test('2 더하기 2는 4', () => {
expect(2 + 2).toBe(4);
})
test('1 더하기 1는 2', () => {
expect(1 + 1).toBe(2);
})
test('3 곱하기 5는 15', () => {
expect(3 * 5).toBe(15);
})
})
Light.js
파일과 Light.test.js
파일을 생성한다.
Light 컴포넌트에는 간단한 버튼과 텍스트 콘텐츠를 작성해놓는다.
Light.test.js
에서 Light 컴포넌트를 import 해온다.
@testing-library/react
의 { render, screen }
또한 import 해온다.
import { render, screen } from '@testing-library/react';
import Light from './Light';
it('renders Light Component', () => {
//테스트 할 컴포넌트를 render
render(<Light name="전원" />);
//props가 올바르게 표시되어 있는지 확인
const nameElement = screen.getByText(/전원 off/i);
expect(nameElement).toBeInTheDocument();
})
it('off button disabled', () => {
render(<Light name="전원" />);
const offButtonElement = screen.getByRole('button', { name: 'OFF' });
//toBeDisabled
expect(offButtonElement).toBeDisabled();
})
it('on button enable', () => {
render(<Light name="전원" />);
const onButtonElement = screen.getByRole('button', { name: 'ON' });
//부정은 not.toBeDisabled
expect(onButtonElement).not.toBeDisabled();
});
it('change from off to on', () => {
render(<Light name="전원" />);
const onButtonElement = screen.getByRole('button', { name: 'ON' });
//버튼 클릭 이벤트 테스트는 fireEvent를 사용한다.
fireEvent.click(onButtonElement);
expect(onButtonElement).toBeDisabled();
})