DCI(Describe-Context-It)
DCI(Describe-Context-It) style with kotest
DCI(Describe-Context-It)란?
Describe-Context-It 패턴
은 테스트 코드 작성 시 계층적으로 구조화된 BDD(Behavior-Driven Development) 스타일을 따릅니다.
- Describe: 테스트 대상 또는 유닛의 기능을 설명합니다.
- Context: 특정 시나리오나 조건을 설정합니다.
- It: 실제 기대되는 동작을 정의합니다.
특징 | 설명 |
---|---|
구조 | Describe → Context → It |
초점 | 테스트 단위나 클래스 기반 구조화, 계층적 테스트 명확성 강조 |
사용 목적 | 특정 클래스나 메서드의 동작, 유닛 테스트 및 기능 테스트에 적합 |
장점 | 계층적으로 구성된 명세를 통해 테스트 의도를 빠르게 파악 가능 |
구체적 역할 | Describe: 대상 설명 Context: 조건 설정 It: 동작 및 기대 결과 정의 |
sample code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
class CalculatorSpec : DescribeSpec({
describe("Calculator") {
context("addition operation") {
it("should return the correct sum when two positive numbers are added") {
val result = 2 + 3
result shouldBe 5
}
it("should return the correct sum when one number is negative") {
val result = -2 + 3
result shouldBe 1
}
}
context("subtraction operation") {
it("should return the correct difference when two positive numbers are subtracted") {
val result = 5 - 3
result shouldBe 2
}
it("should return the correct difference when one number is negative") {
val result = 5 - (-3)
result shouldBe 8
}
}
}
})
BDD
BDD는 일반적으로 테스트의 목적을 사용자 스토리와 연관 짓고 아래와 같이 구성합니다.
- Given: 초기 상태를 설정 (문맥)
- When: 동작 수행
- Then: 결과 검증
특징 | 설명 |
---|---|
구조 | Given → When → Then |
초점 | 사용자 행동(Behavior)와 기대 결과를 명확히 분리하며 스토리 중심의 테스트 작성 |
사용 목적 | 시나리오 기반의 행동 테스트, 사용자 관점에서 애플리케이션의 요구사항을 검증 |
장점 | 시나리오와 비즈니스 로직을 명확히 표현 가능, 개발자와 비기술자 간 커뮤니케이션 용이 |
구체적 역할 | Given: 초기 조건 정의 When: 동작 수행 Then: 결과 검증 |
sample code
1
2
3
4
5
6
7
8
9
10
11
12
13
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
class CalculatorBehaviorSpec : BehaviorSpec({
given("a calculator") {
`when`("adding two positive numbers") {
val result = 2 + 3
then("it should return the correct sum") {
result shouldBe 5
}
}
}
})
DCI vs BDD
BDD 스타일과 DCI의 차이를 아래와 같이 정리할수 있습니다.
특징 | DCI (Describe-Context-It) | BDD (Given-When-Then) |
---|---|---|
초점 | 테스트 단위나 클래스 기반 구조화 | 사용자 행동(Behavior)와 기대 결과 중심 |
의미적 계층 | Describe → Context → It | Given → When → Then |
결합 여부 | 테스트 문맥과 결과를 계층적으로 나눔 | 행동과 기대 결과를 명확히 분리 |
사용 사례 | 유닛 테스트, 특정 메서드 또는 클래스의 동작 검증 | 사용자 스토리 기반 행동 테스트, 비즈니스 로직 검증 |
문서화 용이성 | 테스트 클래스/메서드 중심 문서화 | 시나리오 중심의 테스트 문서화 가능 |
적합한 테스트 유형 | 단위(Unit), 기능(Functional) 테스트 | 행동(Behavior), 통합(Integration) 테스트 |
spring에서 계층별 사용
controller, service, repository layer에서 어떤 스타일을 사용할지에 대한 소개입니다.
Controller => DCI
Controller는 API 요청과 응답을 다루기 때문에, 다음과 같은 구조로 DCI를 사용하는 것이 적합합니다.
- Describe: API 엔드포인트를 분리.
- Context: 요청의 조건 및 환경 설정.
- It: 기대되는 HTTP 상태 코드 및 응답 데이터 검증.
sample code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
class UserControllerSpec : DescribeSpec({
describe("GET /users") {
context("when there are no users") {
it("should return an empty list with 200 OK") {
// Mock API call and assert response
val response = mockMvc.get("/users").andExpect { status { isOk() } }
response.contentAsString shouldBe "[]"
}
}
}
})
Service => BDD
Service 계층은 비즈니스 로직을 구현하므로 행동 기반(BDD) 테스트가 더 자연스럽습니다.
- Given: 비즈니스 로직을 실행하기 위한 초기 조건 정의.
- When: 특정 동작 수행.
- Then: 기대되는 결과를 검증.
sample code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
class UserServiceSpec : BehaviorSpec({
given("a user exists in the system") {
val user = User(id = 1, name = "John Doe")
`when`("the user is retrieved by ID") {
val result = userService.getUserById(1)
then("the correct user is returned") {
result shouldBe user
}
}
}
})
Repository => DCI
Repository 계층은 데이터베이스 연산에 초점이 맞춰져 있으므로 DCI 스타일로 기능 중심의 테스트가 적합합니다.
- Describe: CRUD 메서드별 테스트 분리.
- Context: 특정 데이터 상태나 조건 설정.
- It: 데이터베이스 작업 결과 검증.
sample code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.collections.shouldContain
class UserRepositorySpec : DescribeSpec({
describe("UserRepository") {
context("when a new user is saved") {
it("should be retrievable from the database") {
val user = User(id = null, name = "Jane Doe")
userRepository.save(user)
val allUsers = userRepository.findAll()
allUsers.map { it.name } shouldContain "Jane Doe"
}
}
}
})
Spring 계층별 테스트 스타일의 적합성
계층 | 적합한 스타일 | 이유 |
---|---|---|
Controller | DCI | Controller는 주로 입력 처리와 응답 반환을 다루므로, 기능 중심의 테스트 구조(Divide) 작성이 적합. API의 각 엔드포인트를 Describe로 분리하고, 요청 조건(Context)과 결과(It)를 명확히 기술. |
Service | BDD | Service 계층은 비즈니스 로직의 중심이므로, 행동(Behavior) 중심으로 작성해야 명세를 이해하기 쉬움. 비즈니스 시나리오를 Given-When-Then으로 나타내는 것이 요구사항 검증에 효과적. |
Repository | DCI | Repository 계층은 데이터 저장 및 조회에 초점이 맞춰져 있으므로, 메서드 단위의 동작을 Describe로 분리하고, 특정 조건(Context) 및 결과(It)를 검증. |
[참고]
https://kotest.io/docs/framework/testing-styles.html
This post is licensed under CC BY 4.0 by the author.