Post

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 → ItGiven → 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 계층별 테스트 스타일의 적합성

계층적합한 스타일이유
ControllerDCIController는 주로 입력 처리와 응답 반환을 다루므로, 기능 중심의 테스트 구조(Divide) 작성이 적합.
API의 각 엔드포인트를 Describe로 분리하고, 요청 조건(Context)과 결과(It)를 명확히 기술.
ServiceBDDService 계층은 비즈니스 로직의 중심이므로, 행동(Behavior) 중심으로 작성해야 명세를 이해하기 쉬움.
비즈니스 시나리오를 Given-When-Then으로 나타내는 것이 요구사항 검증에 효과적.
RepositoryDCIRepository 계층은 데이터 저장 및 조회에 초점이 맞춰져 있으므로,
메서드 단위의 동작을 Describe로 분리하고, 특정 조건(Context) 및 결과(It)를 검증.

[참고]

https://kotest.io/docs/framework/testing-styles.html

This post is licensed under CC BY 4.0 by the author.