앞선 포스팅에서는 테스트 코드란 무엇이며 왜 작성하고, 그 종류는 어떤 것들이 있는지 간단히 알아보았다.
하지만 기본적인 내용들만으로 효율적이고 생산적인 테스트 코드를 작성할 수 있다면 좋겠지만, 테스트 코드는 작성부터 유지보수까지 적지 않은 비용이 들어가는 중요한 요소이다.
그렇다면 우리가 테스트 코드 작성에 대해서 더 공부할 수 있는 것들은 어떤 것들이 있을까?
개발방법론 - TDD와 BDD
TDD
"테스트가 개발을 주도한다"
TDD는 Test-Driven-Development의 약자로, 테스트를 작성한 뒤 해당 테스트를 통과하는 메서드를 작성하는
주객전도(?)를 통해 코드 품질 향상과 지속가능성을 확보하는 개발 방법론이다.
TDD는 아래와 같은 플로우로 개발이 진행된다.
(1) 실패하는 테스트 코드 작성
(2) 테스트 코드를 성공시키기 위한 프로덕트 코드 작성
(3) 코드 리팩토링
TDD에서는 AAA 패턴을 주로 사용하는데, 개발 방법론에 따라 TDD와 BDD에서 주로 사용하는 디자인 패턴들의 특징도 각각의 결이 다르다는 점을 염두에 두고 보는 것이 좋을 것 같다.
패턴들의 전체적인 짜임새가 비하여 대충 보면 패턴들간의 큰 차이점을 눈치채기 어렵기 때문이다.
BDD
BDD는 Behavior-Driven-Development의 약자로, 행동 주도 개발이라는 뜻을 가지고 있다.
- 비즈니스 로직 중점
- 테스트 코드는 개발자/비개발자 모두 이해하기 쉽도록 작성
TDD에서 파생된 개발 방법론으로, Behavior(사용자의 행동)은 곧 UseCase(Domain)과 직결되므로 설계 단계에서 도메인 기반(Domain Driven Design)으로 설계하게 된다.
안드로이드의 경우 클린 아키텍처를 사용하는 프로젝트에서 DDD를 적용하는 경우가 많은데,
그러한 프로젝트들에서 테스트 코드 또한 DDD 설계하게 된다면
- 프로덕트 코드와 테스트 코드의 일관성을 유지
- 많은 코드와 나뉘어진 모듈로 인해 이해가 어려운 플로우를 한 눈에 파악
위와 같은 장점을 얻을 수 있다.
테스트 코드 패턴
우리가 일반적인 코드를 작성할 때에도 다양한 디자인 패턴을 적용하듯, 테스트 코드를 작성할 때에도 적용할 수 있는 다양한 디자인 패턴들이 있다.
각 패턴들은 각각의 특징과 장단점을 가지고 있으며 개발쪽의 모든 요소들이 그렇듯,
"이 패턴을 사용해서 테스트 코드 작성하는게 최고지!"라는 명쾌한 답은 없다.
매번 그렇듯 개발자는 이러한 요소들을 적재적소에 잘 사용하면 된다.
(사람들이 주로 사용하는 패턴 또한 요즘은 주로 이런 패턴을 사용해서 테스트를 작성한다 정도로 이해할 것)
✔️ AAA 패턴
AAA 패턴이란 Arrange-Act-Assert의 약자이며, TDD에서 주로 사용하는 패턴이다.
AAA 패턴은 아래와 같이 3단계로 이루어져 있다.
- Arrange : 테스트 조건 설정
- Act : 테스트하고자 하는 행동 실행
- Assert : Act에서 도출된 결과 검증
// Kotest : FunSpec
test("샘플 테스트") {
// Arrange
val x = 10
val y = 20
val z = 30
val god = 5999
// Act
val xyz = x * y * z - god
// Assert
xyz shouldBe 1
}
사실 AAA, DCI, GWT 모두 주석만 다른거 아니야? 라고 생각하기 쉽상인데, 패턴마다 나뉜 영역이나 작성법(표현법)이 다르다.
AAA를 적용한 TDD에서는 테스트 코드를 보고 그걸 통과할 수 있는 프로덕트 코드를 작성해야하는 것이니만큼 개발 문서라고 생각하고 작성해야 한다.
✔️ Given-When-Then 패턴
Given-When-Then 패턴은 BDD에서 주로 사용되는 테스트 패턴이다.
- Given : 테스트 시나리오에 필요한 환경 설정
- When : 테스트할 행동 설정
- Then : 행동(메서드) 결과 검증
// Kotest - BehaviorSpec
Given("사용자가 SignIn 페이지에서 ID와 PW를 입력하고") {
val id: String = "dev"
val pw: String = "ved"
When("로그인 버튼을 누르면") {
val expectedResult = userRepoImpl.getUser(User(id, pw))
Then("UserToken을 반환한다") {
expectedResult shouldBe UserDto(
id = "dev",
pw = "ved"
)
}
}
}
AAA패턴은 전제조건과 수행할 행동, 그에 따른 결과를 문서화하듯 철저하게 분리한다면
GWT패턴은 하나의 테스트 케이스를 기반으로 그것을 플로우 다이어그램 그리듯 작성하는 것이 가장 큰 차이이다.
그로 인해서 AAA 패턴은 개발자가, GWT 패턴은 비단 개발자 뿐 아니라 다른 사람들 또한 이해하기 쉬운 테스트 코드를 지향하게 된다.
✔️ DCI 패턴
DCI 패턴이란, Describe-Context-It의 약자이다.
Describe : 테스트 대상 명시
Context : 테스트할 상황 설명
It : 테스트 대상의 행동 설명
// Kotest - DescribeSpec
describe("테스트 대상이") {
context("어떤 상황에서") {
it("어떻게 행동해야 한다") {
// . . .
}
}
}
Kotest를 사용하면서도 매번 BehaviorSpec만 사용해서 이 패턴은 실무에 적용해본 경험이 없으나, 조만간 사이드 프로젝트에 적용해 볼 예정이다.
견문을 넓히려면 모든 Spec을 적재적소에 사용하는 것이 좋겠지만, Behavior가 제일 직관적이라 끌리는 건 어쩔 수 없는 것 같다.
대세
일반적으로 TDD를 사용하지 않으면서 Kotest를 사용하는 사람들은 BehaviorSpec을 이용한 계층적인 Given-When-Then 패턴을 주로 사용하는 것 같고,
TDD를 적용한 프로젝트의 경우에는 AAA 패턴을 작성하는 것이 더 좋을 것으로 생각된다.
'개발 > Kotlin' 카테고리의 다른 글
[Kotlin] 타입 소거와 Star projection (0) | 2024.06.27 |
---|---|
[Kotlin] 테스트 코드 (1) - 테스트 코드란? (0) | 2023.06.28 |
[안드로이드] 테스트 코드 - JUnit의 예외 처리 (expected, assertThrows, doThrow) (0) | 2023.06.26 |
Singleton 패턴이란? (0) | 2022.12.01 |
연산자 개수에 따른 차이(&, |와 &&,||의 차이) (0) | 2022.07.25 |