TDD
TDD란?
Test-Driven-Development(테스트 주도 개발)로 개발(코드 작성)전에 테스트 코드를 먼저 작성하여 테스트가 개발을 이끌어 나가도록 하는 방식
"테스트 코드를 먼저 작성하는, 즉 구현보다 인터페이스와 스펙을 먼저 정의함으로써 개발을 진행하는 방식이다."
TDD 순서
Red(실패) → Green(성공) → Refactoring(리팩토링) 과정을 반복하는 사이클을 갖는다.
기능 요구사항을 바탕으로 테스트 코드 작성
테스트 코드 실행 (Fail) → 아직 구현체가 없으므로 당연히 실패
작성된 테스트가 통과할 수 있도록 간단한 샘플 코드 작성
테스트 코드 실행 (Success)
3, 4번 단계를 반복하며 코드를 개발
작성한 테스트를 기반으로 코드를 안전하게 리팩토링
TDD 원칙
실패하는 테스트를 먼저 작성하라 → 즉, 미리 구현하지말고 테스트를 먼저 작성(요구사항 분석)한 후에 기능을 구현해야 한다.
테스트를 통과하기 위해 필요한 만큼의 코드만 작성하라 → 미래에 필요할것 같은 구현을 지레짐작하여 불필요한 코드를 작성하는 오버엔지니어링을 방지한다.(YAGNI 원칙)
가장 간단한 문제부터 구현하라 → 간단한 기능부터 차근차근 구현하며 전체적인 기능을 완성해 나가는 것이 중요
코드 커버리지 100%를 목표로 하지 말라 → 테스트 코드를 작성하고 코드를 검증하는 것이 목적이 되어야 한다
TDD 장점
테스트 코드를 작성하기 위해서는 요구사항을 먼저 분석해야 하므로 모든 요구사항(목표)에 대해 이해하고 점검하는 시간을 갖게 된다.
예외 케이스를 미리 고민하고 정리하는 과정을 통해 버그가 생기는 것을 사전에 방지 할 수 있다.
테스트가 쉬운 코드를 작성함으로써 사용자 입장에서 코드를 작성할 수 있다.
인터페이스 위주의 코드 작성
시스템 전반적인 설계 향상
개발 집중력 향상
설계에 대한 피드백이 빠르다.
개발에 자신감이 생긴다
TDD 단점
TDD는 새로운 개념 및 프로세스를 배워야하므로 학습 곡선이 높을 수 있다.
테스트를 작성 및 실행하는 시간, 유지보수 시간 등의 부가적인 시간이 필요하므로 전체 개발 시간이 증가할 수 있다.
의미있는 테스트 작성하기
테스트 해야할 것
기능 테스트에 초점을 맞추고, 사용자 관점의 유저플로우를 통해 올바르게 동작하는지 테스트
사용자 요구사항이 모두 테스트 케이스화 되었는지
정상적인 입력값이 들어온 경우 어떻게 응답하는지
정상적이지 않은 입력이 들어온 경우(엣지케이스) 어떻게 응답하는지
테스트하지 않아도 되는 것
단순히 테스트 커버리지를 높히기 위해 모든 라인을 테스트를 하는 건 낭비다.
기능이 단순한 것
UI와 관련된 테스트
Jest
자바스크립트 환경 테스트 도구
npm i -D jest @types/jest @swc/core @swc/jest \
jest-environment-jsdom \
@testing-library/react @testing-library/jest-dom
Jest 특징
Test Runner & Assertion 통합 라이브러리
Zero config: 복잡한 설정이 없다.
Snapshots 제공: 어떠한 오브젝트 또는 현재의 상태를 캡쳐할 수 있는 기능
Isolated: 독립적이라 테스트 수행하는 속도나 성능이 뛰어남
Great API: 다양한 테스트 api 제공
with Typescript
typescript(swc)와 같이 사용하려면 아래 코드와 같이 추가 설정이 필요하다
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: [
'@testing-library/jest-dom/extend-expect',
],
transform: {
'^.+\\.(t|j)sx?$': ['@swc/jest', {
jsc: {
parser: {
syntax: 'typescript',
jsx: true,
decorators: true,
},
transform: {
react: {
runtime: 'automatic',
},
},
},
}],
},
};
BDD 스타일 테스트 코드 샘플
BDD 스타일 → 대상-행위 중심으로 테스트를 조직화하는 방식.
describe('add', () => {
context('with no argument', () => {
it('return zero', () => {
expect(add()).toBe(0);
});
});
context('with only one argument', () => {
it('return the same number', () => {
expect(add(1)).toBe(1);
});
});
context('with only two argument', () => {
it('return the sum of two numbers', () => {
expect(add(1, 2)).toBe(3);
});
});
context('with only three argument', () => {
it('return the sum of three numbers', () => {
expect(add(1, 2, 3)).toBe(6);
});
});
context('with mutliple argument', () => {
it('return the sum of them numbers', () => {
expect(add(1, 2, 3, 4, 5, 7, 8, 9, 0)).toBe(39);
});
});
});
setupTests.ts
테스트 환경에서 해당 파일의 코드를 모든 테스트 파일에 포함시키는 역할을 한다.
코드의 중복을 피하고 깔끔하게 유지하기 위해 사용한다.
Coverage
특정 테스트의 커버리지 지표 확인하기
npm test -- --testMatch="<rootDir>/src/[테스트 파일]" --collectCoverage --collectCoverageFrom="[테스트 대상]"
Last updated