👀 이번시간에는 TDD는 무엇이며, TDD 중에서 단위테스트도 직접 해보자!
1. TDD(Test Driven Development)?
1) TDD란?
- TDD(테스트 주도 개발)은 말 그대로 개발보다 테스트를 우선 실행하는 걸 말한다.
- 그림1은 기존 개발 프로세스로 설계 → 개발 → 테스트 순으로 진행된다.
- 만약 테스트 결과 실패를 하게 되면 설계를 수정하고 개발 코드를 변경하므로 비용이 높다.
- 하지만 그에 비해 그림2(TDD)는 개발자는 먼저, 요구사항에 맞는 테스트 케이스를 작성한다.
- 그 다음, 테스트 케이스를 통과를 위한 코드를 작성하고, 해당 코드를 리팩토링하는 방식이다.
2) TDD의 장점
(1) 개발 목적에 집중이 가능하다.
💡 내가 작성하는 코드에 대한 이해, 문제 구체화가 가능하다.
- 개발자는 설계 → 개발 → 테스트를 거치는 기존 프로세스가 아닌, 설계 → 테스트 → 개발 프로세스를 거친다.
- 그렇기 때문에 테스트케이스를 작성할 때, 본인에게 주어진 문제를 구체적으로 이해해야한다.
- 이러한 이해는 곧, 본인이 어떤 목적으로 해당 코드를 작성하는지 알 수 있게 한다.
(2) 비용을 감소시킬 수 있다.
💡 설계 수정으로 인한 비용을 줄일 수 있다.
- 앞서 살펴본 그림을 보면, 기존 프로세스는 테스트가 실패가 하면 설계부터 다시 시작해야한다.
- 이는 시간, 인력 등에 대한 비용이 증가를 초래한다.
- 하지만, 그에 비해 TDD는 개발자의 구체적인 문제 이해를 바탕으로 테스트 케이스를 우선 작성한다.
- 그렇기 때문에 개발상 문제가 발생하더라도, 재설계보다는 테스트코드 리팩토링 정도에 그칠 수 있다.
- 이는 개발에 앞서 완성도 높은 설계가 진행했기에, 큰 비용 증가를 방지할 수 있다.
(3) 재사용 가능한 코드를 만들 수 있다.
💡 TDD는 코드의 재사용성을 보장한다.
- TDD를 우선 진행하기 위해서는 우리는 기능을 모듈화 해야한다.
- 모듈화는 코드는 재사용성이 높일 뿐 아니라, 만약 코드에 문제가 생겨 제거를 해도 전체 소프트웨어 대한 영향이 적다.
3) TDD 기법 종류
(1) 수동 테스트
높은 실행 비용, 사용자 경험에 기반한 테스트
- QA 담당자가 UI를 활용하여 각 기능을 테스트한다.
- 사람이 테스트하기 때문에 사용자와 유사한 경험으로 테스트가 가능하다.
- 이 테스트를 위해서는 모든 구현이 완료되어 있어야 한다.
- 사람이 진행하기 때문에 실행비용이 높고 QA 담당자의 역량에 따라 결과 변동이 크다.
- 결과 변동을 줄일 수 있는 방법으로는 QA 명세를 작성하는 방법도 존재한다.
(2) 테스트 자동화
낮은 실행 비용, 개발자 역량에 영향을 받는 테스트
- 사람이 테스트하지 않고 시스템이 코드를 검증한다.
- 테스트 코드 작성을 위한 비용이 발생하지만 수동 테스트에 비해 신뢰도는 높다.
- 단, 테스트 코드 작성&유지보수의 경우 개발자의 역량에 크게 영향을 받는다.
(3) 인수 테스트
높은 비용, 높은 신뢰도를 가진 테스트
- 배치된 시스템을 대상으로 테스트가 진행된다.
- 전체 시스템에 이상이 없는지 검증하기에 신뢰도가 매우 높다.
- 단, 코드 작성, 관리, 테스트까지에 들어가는 비용이 높다.
- 또한 전체 시스템을 테스트하기에, 개발자 입장에서는 피드백의 질이 떨어질 수 있다. (문제를 파악할 수는 있지만, 개발한 코드에 문제인지 전체 시스템에 문제인지 원인을 찾기 어려움)
(4) 단위 테스트
낮은 비용, 높은 피드백을 보장하는 테스트
- 가장 작은 단위(메소드, 함수)별로 테스트를 진행한다.
- 단위별로 진행하기에 테스트 작성, 관리, 실행 비용이 낮은 편이다.
- 단위별로 테스트 하기에, 개발자 입장에서는 피드백의 질이 높아진다.
- 단 인수 테스트와 달리 전체 시스템의 이상여부는 판단하기 어렵다.
4) TDD, 단위 테스트 해보기
💡 앞서 우리는 여러 테스트 기법을 알아보았는데, 이 중 jest를 이용하여 단위테스트를 해보자!
(1) 기본 설정하기
npm init
을 먼저 해준다.
npm init
- 테스트 도구인 jest, 랜덤문자를 생성하는 @faker-js/faker를 설치한다.
npm --save-dev jest
npm --save-dev @faker-js/faker
npm test
시 jest를 실행하도록 해주었다.
// package.json
"scripts": {
"test": "jest --watch"
},
(2) 구현 목적 확인하기
🗣 “hello, 문자” 문장에서 문자를 마스킹 문자(*)로 변경해주세요
- 함수는 두개의 인자(문장, 특정 문자)가 필요하다.
- 문장에서 특정문자를 마스킹 문자로 변경해야 한다.
(3-1) 테스트 케이스 작성하기, 나열하는 방법
- 구현 목적에 맞는 여러 테스트 케이스를 나열식으로 작성하는 방법이다.
// index.test.js
const sut = require("./index");
test('sut transform "hello world", "world" to "hello *****"', () => {
const result = sut("hello world", "world");
expect(result).toBe("hello *****");
});
test('sut transform "hello apple", "apple" to "hello *****"', () => {
const result = sut("hello apple", "apple");
expect(result).toBe("hello *****");
});
test('sut transform "hello bee", "bee" to "hello ***"', () => {
const result = sut("hello bee", "bee");
expect(result).toBe("hello ***");
});
(3-2) 테스트 케이스 작성하기, 반복문 쓰는 방법
- 앞선 테스트 케이스를 나열식이 아닌 반복문으로 변경한 코드이다.
- 반복문을 쓰면, 코드 길이는 줄어들지만 테스트가 실패한 경우에 대한 입력값을 알 수 없다.
- 또한, 우선 실행된 테스트가 실패하면, 그 이후 경우에 대한 테스트가 불가하다.
test('sut correctly works"', () => {
for (const source of [["hello world", "world"], ["hello apple", "apple"], ["hello bee", "bee"]]) {
const result = sut(source[0], source[1]);
const expected = `hello ${"*".repeat(source[1].length)}`;
expect(result).toBe(expected);
}
});
(3-3) 테스트 케이스 작성하기, ParameterizedTest
- ParameterizedTest는 하나의 테스트 메소드로 여러 개의 파라미터에 대해서 테스트 가능하다.
- 코드 중복은 줄이되 테스트 품질 보장 가능하다.
test.each`
source | bannedWord | expected
${"hello world"} | ${"world"} | ${"hello *****"}
${"hello apple"} | ${"apple"} | ${"hello *****"}
${"hello bee"} | ${"bee"} | ${"hello ***"}
`('sut transforms "$source", "$bannedWord" to "$expected"', ({ source, bannedWord, expected }) => {
const result = sut(source, bannedWord);
expect(result).toBe(expected);
});
(3-4) 테스트 케이스 작성하기, 랜덤값
- 이번에는 코드 중복을 줄이고 랜덤값을 테스트할 수 있게 변경해보았다.
- 랜덤값의 경우, 앞서 설치한 faker 라이브러리를 이용하였다.
describe("given banned word", () => {
const bannedWord = faker.lorem.word();
const source = `hello ${bannedWord}`;
const expected = `hello ${"*".repeat(bannedWord.length)}`;
test(`${bannedWord} then invoke sut then it returns ${expected}`, () => {
const result = sut(source, bannedWord);
expect(result).toBe(expected);
});
});
(4) 개발하기
- 앞선 테스트 케이스에 따라 함수를 만들고 재테스트를 해보았다.
- 만약 해당 코드의 테스트 결과를 보고 싶다면, 이 링크를 보자 🙂
// index.js
function refinetText(s, text) {
s = s.replace(text, "*".repeat(text.length));
return s;
}
module.exports = refinetText;
반응형
'개발 기술 > 개발 이야기' 카테고리의 다른 글
[JS] DOM을 감시하는, MutationObserver (0) | 2022.04.17 |
---|---|
eslint 적용시 이슈(rules 무시, 우선순위) (0) | 2022.04.12 |
Promise.allSettled? (0) | 2022.03.13 |
NVM 버전 설정 쉽게 하기(with. nvmrc) (0) | 2022.02.20 |
nvm으로 node버전 관리하기(mac) (0) | 2022.02.15 |
댓글