테스트 주도 개발(Test-driven Development TDD)은 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 방법론(또는 프로세스) 중 하나입니다.
TDD는 단순한 설계를 장려하고 자신감을 불어넣어 주는 역할을 합니다.
TDD = TFD(Test First Developent) + 리팩토링
1. 빨강 - 실패하는 작은 테스트를 작성합니다. 처음에는 컴파일조차 되지 않을 수 있지만 일단 작성합니다.
2. 초록 - 가능한 빨리 테스트가 통과하게끔 만듭니다. 테스트가 통과하기 위해서 어떠한 죄악을 저질러도 좋습니다.
3. 리팩터링 - 초록 단계에서 일단 통과하게만 작성한 코드를 완전한 형태로 리팩터링합니다. (매직 넘버, 리터럴 제거 / 중복 제거 등 / 도메인 모델 검증 함수 추가)
죄악이란 기존 코드를 복사해서 붙이기, 테스트만 간신히 통과할 수 있도록 테스트 하는 함수가 무조건 특정 상수만 반환하도록 구현 하는 등의 행위를 의미합니다.
즉, TDD 사이클은 Red - Green - Refactor의 반복으로 이루어져 있습니다.
만약, 페어프로그래밍 중이라면 사이클의 각 단계를 번갈아가면서 하는 것도 좋습니다.
또한, TDD는 리팩터링에 대한 근거를 만들어 나갈 수 있습니다.
빨강, 초록 단계에서 테스트가 가능한 빠르게 통과할 수 있게끔 코드를 작성하고 나니, 해당 코드를 고쳐야 할 이유와 근거를 찾는 자신의 모습을 발견할 수 있습니다.
예를 들어, 6개의 숫자로 이루어져 있는 Lotto 클래스의 객체를 생성하는 코드를 TDD로 진행해보겠습니다.
다만, TDD에서 이 정도는 스킵해도 괜찮다고 생각되는 과정은 건너 뛰기도 하는데 이를 퀀텀 점프라고 합니다.
예를 들어, 이미 require() 메서드를 알고 있는데, 굳이 일단은 if문으로 검증 로직을 작성하고 리팩터링할 필요가 없다는 것입니다.
TDD(Test-driven Development)의 마지막 알파벳 D의 의미보다 중요한 것!
'TDD가 왜 어렵게 느껴질까?'라고 느껴진다면 TDD의 이전 단계를 생각해볼 필요가 있습니다.
TDD의 마지막 알파벳의 의미는 Development로 개발을 의미합니다.
그런데 이 D를 Development가 아닌, 설계(Design)으로 생각해본다면 어떨까요?
우리는 개발을 하기 이전에 더 중요한 작업인 설계(Design)라는 과정을 거칩니다.
그리고 개발은 설계 이후에 이루어지는 작업으로, 설계가 제대로 이루어지지 않는다면 개발이 힘들어지는 것은 당연할 뿐더러 TDD 방법론을 적용하는 것은 말하지 않아도 느낄 수 있을 것입니다.
그렇기 때문에 TDD에서 개발만큼 중요한 설계에 대해서도 깊게 고민해볼 필요가 있습니다.
Lotto 미션을 통한 TDD 알아보기
6개의 숫자들로 Lotto 객체를 생성했을 때 예외가 발생하지 않는 Test Code
Red
숫자 6개가 주어지고, 로또 객체를 생성했을 때 예외가 발생하지 않는 테스트 코드를 작성합니다.
여기서 우리는 Lotto 객체가 없기 때문에 컴파일 에러가 발생합니다.
Green
테스트를 통과하도록 만들어주기 위해, Lotto 클래스를 생성해줍니다.
Refactor
테스트만 통과하게끔 코드를 작성하다보니 numbers를 프로퍼티로 만들어주지 않았습니다.
앞에 private val 를 붙여 클래스 내부에서 활용 가능하게끔 만들어줍니다.
6개가 아닌 숫자들로 Lotto 객체를 생성했을 때 예외가 발생하지 않는 Test Code
Red
Lotto 객체를 생성했을 때 예외가 발생하는 테스트 코드를 작성해보겠습니다.
이 코드는 숫자의 개수가 5개이기 때문에 Lotto 객체 생성시 IllegalArgumentException이 발생하기를 기대합니다.
하지만, 테스트 코드 실행시 Lotto에는 예외가 발생하는 코드가 없기 때문에 실패합니다.
이제 Green 단계로 넘어가서 가능한 빨리 테스트 코드가 성공하게끔 만들어줘야 합니다.
Green
테스트 코드가 가능한 빨리 성공하게끔 만들어주기 위해 init 블럭 내에 IllegalArgumentException을 던져줍니다.
예외가 발생하기 때문에 테스트 코드가 통과하기는 하였지만, 로또 개수가 6개가 아닐 때에도 예외가 발생 할지에 대해 생각해보아야 합니다.
그렇지 않습니다.
여기서 우리는 코드를 추가해야 하는 하나의 '근거', '이유'를 찾은 것입니다.
이 근거에 따라 리팩토링 단계로 넘어가보겠습니다.
Refactor
numbers 리스트 크기가 6이 아닌 경우 "예외 발생" 이라는 에러 메시지가 담긴 IllegalArgumentException을 발생시키도록 리팩토링하였습니다.
하지만 아직 수정해야 하는 부분이 있습니다.
1. 매직 넘버, 리터럴이 존재합니다. (가독성이 떨어짐)
2. if문 대신 조금 더 Kotlin스럽게 작성할 수 있습니다. (require()를 사용하여 간결하게 작성할 수 있음)
TDD는 이처럼 위와 같이 코드를 수정하거나 추가할 근거를 가지고 개발을 할 수 있다는 장점이 있습니다.
또한, 테스트 코드를 기반으로 로직을 만들어나가기 때문에 각 기능에 대한 자신감과 확실함을 가질 수 있습니다.
원칙
1. 실패하는 단위 테스트를 작성할 때까지 구현 코드(production code)를 작성하지 않는다.
2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
용기
테스트 주도 개발은 프로그래밍을 하면서 나타나는 두려움을 관리할 수 있습니다.
두려움은 개발자를 망설이게 만듭니다.
두려움은 커뮤니케이션을 덜 하게 만듭니다.
두려움은 피드백 받는 것을 피하도록 만듭니다.
두려움은 개발자를 까다롭게 만듭니다.
이 중 어떠한 것도 프로그래밍에 도움이 되지 않습니다.
누군가 TDD는 좋냐, 나쁘냐라고 묻는다면 여기에 정답은 없다고 말할 수 있습니다.
TDD는 수많은 프로그래밍 방법론 중 하나로, 다시 말해 개발을 편리하게 할 수 있도록 도와주는 하나의 도구입니다.
도구를 사용했을 때 작업이 편리해진다면 계속 사용하면 되며,
반대로 도구의 사용이 팀에 불편함을 초래한다면 지양하는 방법을 택할 수 있습니다.
즉, 필요할 때 목적에 맞게 사용할 수 있습니다.
TDD의 장점
1. TDD는 초반 생산성이 느릴 수 있지만, 이후 유지보수 단계까지 고려했을 때는 높은 생산성을 가질 수 있습니다.
나무일 때에는 느리지만, 숲의 관점에서는 빠르다는 의미입니다.
2. 기능에 대한 테스트 코드가 항상 존재하기 때문에, 기능 수정시에도 확신을 가지고 수정할 수 있습니다.
3. 지금까지 아무 이유없이 코드를 추가했다면, TDD에서는 근거를 가지고 코드를 추가할 수 있습니다.
요약
TDD(Test Driven Development)란?
- TDD는 테스트 주도 개발로, 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 방법론이다.
- TDD는 크게 3가지 단계로 이루어진다.
- Red - 실패하는 테스트 코드를 작성한다. 이 때, 컴파일 조차되지 않을 수 있다.
- Green - 가능한 빠르게 테스트 코드가 통과하게끔 수정한다. 이 때 어떠한 죄악을 저질러도 좋다.
- Refactor - 이전 단계를 거치며 발생한 코드의 중복, 매직 넘버 등을 제거한다.
- TDD를 거치면서 자연스럽게 기능 구현 및 코드 수정에 대한 근거를 얻을 수 있다. 예를 들어, Green 단계에서 어떠한 죄악을 저질러서 단순히 테스트 코드가 빠르게 통과하게끔 구현했다면, Refactor 단계에서는 이 코드가 '왜' 통과해야 하는지 근거를 찾도록 생각이 유도된다. 이 과정을 통해 얻은 근거를 바탕으로 코드를 수정할 수 있다.
- 프로덕션 코드를 작성하기 전에, 각 기능에 대한 테스트 코드를 먼저 작성하다보니, 이와 반대되는 상황에서 테스트 코드 작성을 깜빡하는 상황을 방지할 수 있다.
- 각 기능에 대한 테스트 코드가 작성되어 있기 때문에, 기능을 수정할 때 테스트 코드를 믿고 과감하게 도전할 수 있다.
- 작성하고자 하는 테스트 코드가 어떤 기능인지 먼저 고려해야 한다. 즉, Development 이전에 Design 과정을 거쳐야 하므로 설계에 대한 고민을 깊게 하며 더 탄탄한 프로그램을 만들 수 있다.
- TDD를 거치며 만들어진 테스트 코드는 하나의 레퍼런스이다. 특정 기능이 어떠한 목적으로 만들어졌는지, 어떤 경우에 예외가 발생하는지 등을 테스트 코드를 살펴보면 알 수 있다.
- 위와 같은 TDD의 장점은 곧 개발자에게 자신감을 불어넣어 준다. 기능이 변경되었을 때 프로그램이 오동작하지는 않을지 걱정했다면 이제는 먼저 작성해놓은 테스트 코드를 믿고 과감하게 실행에 옮길 수 있을 것이다.
- 하지만 TDD가 항상 좋다고 할 수는 없다. 예를 들어, 데드라인(예를 들어 미션..)이 몇 시간 남지 않은 상황에서 테스트 코드를 작성하겠다고 결과물을 제출하지 못한다면 아무 소용이 없다. 이 때에는 오히려 TDD에 대한 안 좋은 감정만 남게 될 것이다. 결론적으로, TDD도 결국 하나의 개발 방법론이자 도구이기 때문에, 현 상황이 TDD를 적용하기 알맞은 상황인지를 명백히 고려할 필요가 있다.
'대외활동 > 우아한테크코스' 카테고리의 다른 글
[우아한테크코스] 2주차 - 점진적 리팩터링(Incremental Refactoring) (5) | 2023.02.24 |
---|---|
[우아한테크코스] 2주차 - 로또(자동) 미션 회고 및 피드백 (4) | 2023.02.19 |
[우아한테크코스 5기] 1주차 회고 - (양념 반 후라이드 반) 설렘 반 걱정 반 (6) | 2023.02.13 |
[우아한테크코스 5기] 프리코스 4주차 회고 - Kotlin 안드로이드 (2) | 2022.11.23 |
[우아한테크코스 5기] 프리코스 3주차 회고 - Kotlin 안드로이드 (1) | 2022.11.23 |