💡 TDD를 활용한 개발의 장점을 알고, 리팩토링하는 과정을 학습하였습니다.
TDD
- 테스트 주도 개발로 코드를 작성하기 전에 테스트를 먼저 작성하는 개발 방법론입니다.
- 아래와 같은 작업을, 매우 짧은 사이클로 반복한다는 특징이 있습니다.
- [빨강] 실패하는 작은 테스트 작성
- [초록] 최대한 빠른 테스트 통과
- [파랑] 리팩터링 과정을 통해서 중복 & 코드 개선
- TDD의 목적은 코드가 테스트에 지정한 요구 사항을 충족하는지 확인하고, 코드를 리팩토링하기 쉽게 수정하는 것입니다.
TDD 원칙
- 실패하는 단위 테스트를 작성할 때까지 구현 코드를 작성하지 않습니다.
- 컴파일은 실패하지 않지만, 실행이 실패하는 정도로만 코드를 작성합니다.
- 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성합니다.
Kotlin에서의 TDD
- Kotlin은 Java를 포함한 다른 언어에 비해 매우 간결한 언어로, 적은 코드를 통해 기능을 구현할 수 있습니다.
- 코드 수가 적을수록 테스트를 작성하는 시간이 감소하므로 Kotlin은 TDD에 적합한 언어라고도 할 수 있습니다.
TDD 구성하기
- BankAccount(은행 계좌)라는 도메인 클래스를 구성한다고 할 때, TDD를 구현해보겠습니다.
테스트 작성
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class BankAccountTest {
private lateinit var account: BankAccount
@BeforeEach
fun setUp() {
account = BankAccount()
}
@Test
fun testInitialBalance() {
assertEquals(0, account.getBalance())
}
@Test
fun testDeposit() {
account.deposit(100)
assertEquals(100, account.getBalance())
}
}
- 우선 테스트를 작성하였습니다.
- account에는 아래와 같은 두가지 메서드를 가지고 있습니다.
- 입금을 진행하는 deposit()
- 잔액 확인을 진행하는 getBalance()
도메인 클래스 작성
class BankAccount {
private var balance: Int = 0
fun getBalance(): Int {
return balance
}
fun deposit(amount: Int) {
balance += amount
}
}
- 이 후 원하는 도메인 클래스를 생성하였습니다.
- 테스트 코드 작성 시 기대하는 값으로 반환하도록 메서드를 구현하였습니다.
리팩토링 진행
- 도메인 클래스를 작성하였다면, 리팩토링을 진행할 수 있습니다.
- 기능이 적은 클래스이므로, 변수 이름 개선 과 초기화 로직을 분리하는 간단한 리팩토링을 진행하였습니다.
- 또한 예외 처리를 추가하여 유효하지 않은 입력을 방지하고자 하였습니다.
class BankAccount {
private var currentBalance: Int = 0
init {
initialize()
}
private fun initialize() {
currentBalance = 0
}
fun getBalance(): Int = currentBalance
fun deposit(amount: Int) {
require(amount > 0)
currentBalance += amount
}
}
반복하기
- 테스트 코드 작성 → 도메인 로직 구현 → 리팩토링 단계를 거쳤으므로, 다시 테스트 코드를 수정해야 합니다.
- deposit() 메서드를 호출할 때 유효성을 검사하므로, 실패하는 테스트를 작성하여 유효성 검증이 제대로 이루어지는지 확인할 수 있습니다.
@Test
fun testDepositNegativeAmount() {
val exception = assertThrows<IllegalArgumentException> {
account.deposit(-100)
}
assertEquals("Deposit amount must be positive", exception.message)
}
- 이와 같이 리팩토링 과정을 통해 점진적으로 기능을 개선하면서 도메인을 TDD로 구현할 수 있습니다.
결론
- TDD를 활용한다면 좋은 설계를 할 수 있는 밑바탕이 되며, CI/CD에 적용하여 테스트를 자동화할 수 있습니다.
- 다양한 케이스를 먼저 생각하고 개발하면 코드 작성 시 간결하고 좋은 코드를 작성할 수 있습니다.
- 축적된 테스트 코드는 높아진 테스트 커버리지로 버그 수정 및 리팩토링이 수월 해, 유지보수가 높아진다는 큰 장점을 가지고 있습니다.
- 반면에 테스트 코드를 작성하기 위한 러닝커브가 존재합니다.
- Junit, Espresso 등의 API를 학습해야 함
- 기획 변경에 따른 코드가 수정된다면, 지속적인 테스트 코드 수정 역시 불가피할 수 없습니다.