들어가기에 앞서
참고한 자료를 바탕으로 비전문가가 정리한 글이므로 오류가 있을 수 있습니다.
오류에 대한 피드백은 언제든지 환영합니다. 부디 댓글로 알려주시길 바랍니다. 감사합니다.
TDD에 대한 간단 소개
TDD는 실제 코드를 작성하기 전에 테스트 코드 작성을 강조하는 소프트웨어 개발 방법론입니다. 위 이미지에서 나와있듯이, Test Driven Development는 세 가지 단계를 한 사이클로 돌게 됩니다.
•
Red
Red 단계에서는 아직 구현되지 않은 로직에 대한 실패할 테스트 케이스를 작성합니다.
•
Green
Green 단계에서는 테스트를 통과하는 데 필요한 최소한의 코드를 작성합니다.
•
Refactoring
리팩토링 단계에서는 동작을 변경하지 않고 코드를 개선합니다.
이 사이클을 무한정 반복하는 것이 TDD라고 보시면 됩니다.
간단한 단위 테스트 작성 해보기
그럼 이제 위 TDD의 소개에서 나온대로 red-green-refactoring 과정을 진행 해보겠습니다. 간단한 로그인 시스템을 작성할 예정이며, red 단계인 실패 테스트 케이스부터 작성하겠습니다.
Red
// 로그인 테스트
describe("로그인 함수 단위 테스트", () => {
const fakeUserDB:UserDB = {
jeonb: "abcd123",
};
// 로그인 성공 테스트
test("로그인 성공", () => {
const successResult = login("jeonb", "abcd123", fakeUserDB);
});
// 로그인 실패 테스트
test("로그인 실패", () => {
const failureResult = login("jeonb", "wrong_password", fakeUserDB);
});
});
TypeScript
복사
UserDB 라는 타입도 정의하지 않았고, login 함수도 아직 작성하지 않아서 당연히 테스트가 실패하는 모습입니다. 심지어 다른 테스트 파일에서 작성한 함수인 Log를 login으로 잘못 입력한거 아니냐며 친절하게 알려주기도 합니다.
Green
이제 다음 단계인 red를 green으로 바꾸는 작업을 해보겠습니다.
먼저 페이크 데이터베이스의 타입부터 정의하고, login함수를 작성하겠습니다. login함수는 입력값으로 받은 데이터가 데이터베이스 상에서 존재하는 값과 일치하면 true를 반환하고, 일치하지 않는다면 false를 반환합니다.
// 가짜 사용자 데이터 타입 정의
type UserDB = {
[username: string]: string;
};
// 로그인 함수
function login(username: string, password: string, userDB: UserDB): boolean {
if (username in userDB && userDB[username] === password) {
return true;
} else {
return false;
}
}
TypeScript
복사
그런 다음 다시 테스트를 진행하면 아래와 같이 Green으로 바뀌는 모습을 볼 수 있습니다.
하지만, 현재까지 작성한 코드에서는 단언문이 없기에, 이 상태로는 어떤 로직을 추가하든 문법적인 오류만 없다면 테스트는 통과할 것입니다.
리팩토링
테스트는 코드가 기대한 대로 동작하는지를 확인하는 것이기 때문에, 동작을 검증하는 단언문이 반드시 필요합니다. 따라서 expect() 함수를 사용하여 테스트 결과를 검증하는 절차를 추가해보겠습니다.
// 가짜 사용자 데이터 타입 정의
type UserDB = {
[username: string]: string;
}
// 로그인 함수
function login(username: string, password: string, userDB: UserDB): boolean {
if (username in userDB && userDB[username] === password) {
return true;
} else {
return false;
}
}
// 로그인 테스트
describe('로그인 함수 단위 테스트', () => {
const fakeUserDB: UserDB = {
"jeonb": "abcd123"
};
// 로그인 성공 테스트
test('로그인 성공', () => {
const successResult = login("jeonb", "abcd123", fakeUserDB);
expect(successResult).toBeTruthy();
});
// 로그인 실패 테스트
test('로그인 실패', () => {
const failureResult = login("jeonb", "wrong_password", fakeUserDB);
expect(failureResult).toBeFalsy();
});
});
// 테스트 실행 npm test
TypeScript
복사
위의 로그인 성공 및 실패 테스트에서는 로그인 함수에 입력한 유저 아이디와 패스워드가 데이터베이스 상(가짜 데이터)에서의 값과 일치하거나 일치하지 않는다면 각각 성공한 것으로 간주할 것입니다.
예상한 대로 각 테스트는 통과되었습니다. 이번에는 로그인 성공과 실패 테스트에 입력한 값을 서로 바꿔서 입력한다면 어떻게 될까요? 당연히 테스트는 실패해야 합니다.
각각의 테스트에서 예상한(expect) 값이 서로 반대로 false와 true가 나와 테스트가 실패한 모습입니다.
마무리
테스트 코드를 작성하고 코드를 리팩토링하는 과정은 실제 개발 과정에서 매우 중요합니다. 테스트 코드를 통해 코드의 동작을 검증하고, 리팩토링을 통해 코드의 가독성과 유지 보수성을 향상시킬 수 있습니다. 이를 통해 코드의 신뢰성을 높이고 안정성을 확보할 수 있습니다. 테스트 주도 개발(TDD)은 이러한 과정을 체계적으로 수행할 수 있는 방법론 중 하나로, 코드의 품질을 높이고 개발 생산성을 향상시킬 수 있습니다.
위 글에서는 TDD의 핵심인 Red-Green-Refactor 사이클을 따라가면서 간단한 로그인 함수를 개발하는 과정을 설명했습니다. 테스트 코드를 작성하여 기능의 동작을 검증하고, 이를 통과하는 코드를 작성한 후 리팩토링하여 코드의 가독성을 높였습니다.
이러한 과정은 코드의 품질을 높이고 유지 보수성을 향상시키는 데 큰 도움이 됩니다.