본문 바로가기
테스트

kotlin kotest BehaviorSpec 에서 @SpringBootTest 하기

by 콩지영 2024. 9. 13.

새 프로젝트를 코틀린으로 시작하면서 코틀린을 새롭게 배우게 됐다. 이제는 새로운 언어를 배우게 되면 테스트코드로 학습을 하게 되는것 같다. 처음에는 junit5 을 사용하다가 코틀린 진영에서 쓰는 kotest가 있다는걸 알게 됐다. 레퍼런스가 많이 없는 상황에서 spring kotest를 적용하면서 겪은 문제점들과 해결 방법을 적어보려고한다.

 

문제 

1. kotest에서 @SpirngBootTest가 돌아가지 않는다

2. 의존성 주입을 어떻게 받아야 하는지 모르겠다

 

기본 셋팅

1. 의존성 추가

    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.kotest:kotest-runner-junit5:5.8.1")
    testImplementation("io.kotest:kotest-assertions-core:5.8.1")
    testImplementation("io.kotest.extensions:kotest-extensions-spring:1.3.0")

    testImplementation("io.mockk:mockk:1.13.11")
    testImplementation("io.mockk:mockk-jvm:1.13.11")

1. kotest 기본 의존성

2. SpringBootTest를 위한 extensions 

3. mock 관련 라이브러리: 코틀린은 k가 한개 더 붙은 mockk를 쓴다

 

2. SpringBootTest를 위한 설정 추가

import io.kotest.extensions.spring.SpringExtension

//해당 설정이 있어야 kotest시 SpringBootTest가 정상작동함
class TestConfig : AbstractProjectConfig() {

    override fun extensions(): List<Extension> = listOf(SpringExtension)

}

- 해당 설정이 없으면 테스트 내부에서 spring bean들을 주입받지 못한다

- 이 밖에 AbstractProjectConfig 설정들을 오버라이드 해서 테스트시 필요한 공통 로직들을 커스텀할 수 있다.

- 해당 내용은 아래에서 확인할 수 있다

https://kotest.io/docs/extensions/spring.html

 

Spring | Kotest

Kotest offers a Spring extension that allows you to test code that uses the Spring framework for dependency injection.

kotest.io

 

이 설정이 없어도 Spring이 필요하지 않은 테스트는 돌아간다. 

 

3. BehaviorSpec 에서 의존성 주입 받기

BehaviorSpec 을 이용하면 자연스럽게 BDD 테스트를 사용할수 있도록 테스트코드를 작성할 수 있다. 보통 아래처럼 생성자에 람다를 넘겨서 사용한다. 

class ProductTest : BehaviorSpec({

    Given("given...") {
        When("when...") {
            Then("then...") {
                //empty
            }
        }
    }
})

 

하지만 스프링 테스트에서는 이렇게 작성하면 IOC 컨테이너의 DI 기능을 사용할 수 없다

구현부를 가보면 바로 알수가 있는데

여러 **Spec 들은 생성자로 받은 body 함수를 init 함수에서 실행한다. 

init 함수는 생성자 바로 다음에 실행되는 "함수" 이기 때문에 여기서 선언하는 변수들은 모두 로컬변수가 된다.

로컬변수에는 당연히 DI를 할 수 없다

인텔리제이에서도 해당 경우에 빨간줄을 띄운다

 

따라서 스프링 테스트에서는 이렇게 작성해줘야 한다 

기존 생성자에 넣던 함수를 init 함수에 선언한다

 

@SpringBootTest
class ProductTest : BehaviorSpec() {

    @Autowired
    lateinit var productTestService: ProductTestService

    init {
        Given("given...")
        {
            When("when...") {
                Then("then...") {
                    //empty
                }
            }
        }
    }
}

 

이렇게하면 정상적으로 스프링 테스트가 돌아간다