TDD란 Test Driven Development의 약자로 '테스트 주도 개발'이다. 반복 테스트를 이용한 소프트웨어 방법론으로 즉, 테스트 코드를 작성하고 이에 따라 개발을 진행하고, 다시 테스트 코드를 작성하는 것을 반복한다.
먼저 테스트에 대해 알아보고 TDD는 어떻게 진행되는지 알아보자!
소프트웨어 테스트란 다음과 같다.
단위 테스트 Unit test
: 응용 프로그램이 설계된 대로 작동하는지 확인하는 것으로 unit은 소프트웨어의 최소단위, 보통 함수를 가리킴
통합 테스트 Integration test
: 단위 기능이 합쳐진 기능에 대한 테스트로 모듈/기능 간의 인터페이스 결함을 찾기 위해 설계
시스템 테스트 System test
: 위 내용보다 더 큰 개념, 전체 시스템에 대한 동작 테스트
인수 테스트 Acceptance Test
: 시스템이 릴리스 준비가 되었는지 확인하기 위한, 고객이 ok할 수 있는지 판단하기 위한 테스트
그리고 이와 별개로 아래 두가지 테스트가 있다.
이 중 중요한 단위 테스트에 대해 더 알아보겠다.
단위 테스트는 빠르게 수행하고, 격리된 방식(다른 테스트에 영향을 주지 않아야 함)으로, 작은 코드 조각(단위)을 검증하고, 자동화된 진짜 코드이다.
테스트 환경
화이트박스 테스트 : 코드 내부를 들여다보고 테스트(구문/조건/분기 커버리지)하는 것
블랙박스 테스트(권장) : 내부를 모르는 상태에서 입/출력 테스트를 하는 것, 경계값 분석, 동등 클래스 분할 테스트 등
A-A-A 테스트 (Arrange-Act-Assert)
테스트해야 하는 것!! Right - BICEP
좋은 테스트란? A-TRIP
중요한 것은 실패하는 테스트 코드를 먼저 작성하고, 이에 맞게 실제 프로덕션 코드를 작성한다는 것이다!
이렇게 했을 경우 어떤 장단점이 있을까?
TDD로 개발을 했을 때 기존에 본인이 하던 개발 방식을 바꿔야 하고, 중간중간 테스트 코드를 작성하는 시간이 들어 생산성이 떨어진다고 생각할 수 있다. 그럼에도 불구하고 TDD 방법론을 사용해야 하는 이유를 알아보자.
디버깅 시간 단축
유닛 테스트의 가장 큰 이점으로, 어떤 부분에서 에러가 발생했는지 바로 파악할 수 있다.
깔끔한 코드 작성
테스트 코드를 작성 한 후 실제 코드를 작성하므로 테스트 코드에 맞게 깔끔하게 작성할 수 있다.
불안정성 개선
작성한 코드를 지속적으로 테스트함으로써 코드가 가지는 불안정성을 개선할 수 있다.
재설계 시간 단축
테스트 코드를 먼저 작성하기 때문에 개발자가 지금 무엇을 해야하는지 분명히 정의하고 개발을 시작하게 된다.
추가 구현 용이
개발이 완료된 소프트웨어에 어떤 기능을 추가할 때 추가하는 기능이 기존 코드에 어떤 영향을 미칠지 모르지만, TDD의 경우에 유닛 테스트를 자동으로 실행하므로 시간을 단축시킬 수 있다.
TDD 방식을 사용했을 때와 그렇지 않을 때의 effort에 대한 그래프이다. TDD 방식이 처음에는 노력이 더 필요하지만 시간이 지나 프로젝트가 진행되었을 때는 TDD 방식이 더 효율적이다. 각자의 상황에 맞게 TDD를 사용하면 되겠다.
Jest는 페이스북에서 만든 테스팅 라이브러리이다. 사용법을 알아보자!
jest 설치 및 setting
npm install --save-dev jest
// package.json
{
"scripts": {
"test": "jest"
}
}
위와 같이 jest를 dev dependencies로 설치하고, package.json test 설정을 해준다.
babel 설치
npm install --save-dev babel-jest @babel/core @babel/preset-env
Babel은 JavaScript 코드의 변환 및 호환성을 향상시키는 도구로 JavaScript 버전 간 호환성을 유지하고 코드를 변환하는 과정에서 최적화하는 등의 기능을 한다. 이러한 babel과 관련된 모듈을 설치한다.
babel config 설정
// babel.config.json
{
"presets": [
["@babel/preset-env", { "targets": { "node": "current" } }],
]
}
babel.config.json
파일을 만들고 위와 같이 설정 코드를 입력한다.
typescript 관련 설정
npm install --save-dev @babel/preset-typescript
// babel.config.json
{
"presets": [
["@babel/preset-env", { "targets": { "node": "current" } }],
"@babel/preset-typescript",
]
}
나는 프로젝트에 typescript를 사용하고 있어 babel의 typescript 관련 모듈과 세팅을 추가해주었다.
npm install --save-dev @jest/globals
그리고 typescript로 작성된 파일에 jest의 APIs를 사용하기 위해 위의 모듈을 설치해주었다. 사용할 때는 다음과 같이 import
하여 사용할 수 있다.
import {describe, expect, test} from '@jest/globals';
테스트 코드를 작성할 파일은 file_name.test.js
, file_name.test.ts
로 네이밍을 한 후 원하는 테스트 코드를 작성한다. 이후 테스트할 때 npm test
명령어로 모든 테스트 파일을 실행할 수 있다.
아래에서 Matcher를 이용하여 테스트 코드를 작성하는 방법을 알아보자.
기본 문법
test(*'explanation'*, () => {
expect(*'input'*).Matcher(*'output'*);
});
// describe를 사용하면 안에 test 함수를 여러 개 묶을 수 있다.
describe('sign up', () => {
test('adds 1 + 2 to equal 3', () => {
expect(isUniqueUser('sw92522@naver.com')).toBe(3);
});
});
toBe()
: 정확하게 동일한지(주소값) 확인
toEqual()
: object의 값만 같은지 확인 ⇒ 약간 js의 ‘===’와 ‘==’의 느낌
test("object lastDirectory", () => {
expect(path.lastDirectory).toBe("day12");
})
test("object components", () => {
expect(path.components).toEqual(["/", "home", "user", "boost", "camp", "challenge", "day12", "problem.md"]);
})
not
: matcher 앞에 사용 가능 ex) not.toBe()
, .not.toEqual()
toBeNull
: matches only null
toBeUndefined
: matches only undefined
toBeDefined
: is the opposite of toBeUndefined
toBeTruthy
: matches anything that an if
statement treats as true
toBeFalsy
: matches anything that an if
statement treats as false
toBeGreaterThan
toBeGreaterThanOrEqual
toBeLessThan
toBeLessThanOrEqual
toMatch
: 정규식과 매치되는지 확인
test('but there is a "stop" in Christoph', () => {
expect('Christoph').toMatch(/stop/);
});
toContain
: array or iterable contains에 특정 아이템 포함여부 확인
const shoppingList = [
'diapers',
'kleenex',
'trash bags',
'paper towels',
'milk',
];
test('the shopping list has milk on it', () => {
expect(shoppingList).toContain('milk');
expect(new Set(shoppingList)).toContain('milk');
});
toThrow
: 예외 발생 확인test("create object include special letters", () => {
expect(() => new Path('C:\\home\\user\\boost\\camp*\\day8\\문제.md')).toThrow();
})
https://wooaoe.tistory.com/33
https://media.fastcampus.co.kr/knowledge/dev/tdd/
https://inpa.tistory.com/entry/QA-%F0%9F%93%9A-TDD-%EB%B0%A9%EB%B2%95%EB%A1%A0-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C
https://semaphoreci.com/blog/economics-of-tdd
https://jestjs.io/docs/getting-started