Kotest Spring 확장(SpringExtension)

Kotest는 종속성 주입을 위해 Spring 프레임워크를 사용하는 코드를 테스트할 수 있는 Spring 확장 기능을 제공한다.

Spring Extension

의존성 추가

이 확장 기능을 사용하려면 테스트 컴파일 경로에 io.kotest.extensions:kotest-extensions-spring 모듈을 추가해야 한다. 최신 버전은 maven central에서 확인할 수 있다.

먼저 Spring에 kotest를 사용하려면 아래 의존성을 추가해야 한다.

testImplementation("io.kotest.extensions:kotest-extensions-spring:1.0.0")

참고: maven 그룹 ID는 핵심 테스트 프레임워크(io.kotest.extensions)와 다르다.

Configuration

모든 테스트에 대해 SpringExtension을 적용하려면 글로벌 설정을 통해서 설정이 가능하다.

class GlobalProjectConfig : AbstractProjectConfig() {
    override fun extensions() = listOf(SpringExtension)
}

Spring 확장을 사용하려면 모든 테스트 클래스 또는 테스트 클래스별로 활성화해야 한다.

전역으로 활성화하려면 프로젝트 구성에서 SpringExtension을 등록해야 한다:

class ProjectConfig : AbstractProjectConfig() {
   override fun extensions() = listOf(SpringExtension)
}

때로는 SpringExtension을 전역으로 활성화하지 않고 테스트를 사용하고 싶을 수도 있다. 이런 경우 글로벌 설정을 하지 않고 SpringExtension을 사용하는 테스트 클래스에서만 extensions를 설정해서 사용할 수도 있다.

테스트 클래스별로 활성화하는 방법은 아래와 같다:

class SpringTest : FunSpec() {
    override fun extensions() = listOf(SpringExtension)
}

Spring에 사용할 구성 클래스를 알려주려면 Spec 클래스에 @ContextConfiguration 어노테이션을 추가해야 한다. 이 어노테이션은 Spring @Configuration 어노테이션으로 어노테이션된 클래스를 가리켜야 한다. 또는 @ActiveProfiles를 사용하여 특정 애플리케이션 컨텍스트 파일을 가리킬 수 있다.

생성자 주입

생성자 주입의 경우, 자동 스캔이 활성화되어 있다고 가정하고 스프링 모듈이 빌드에 추가될 때 Kotest는 SpringAutowireConstructorExtension을 자동으로 등록한다(프로젝트 구성 참조). 자동 스캔이 비활성화되어 있으면 프로젝트 구성에서 수동으로 확장자를 로드해야 한다.

이 확장은 Spec 인스턴스를 생성하기 위한 각 호출을 가로채고 기본 생성자에서 선언된 Bean을 자동 연결한다.

다음 예제는 기본 생성자에 UserService라는 서비스가 필요한 테스트 클래스이다. 이 서비스 클래스는 @Component로 어노테이션된 일반 스프링 빈이다.

@ContextConfiguration(classes = [(Components::class)])
class SpringAutowiredConstructorTest(service: UserService) : WordSpec() {
  init {
    "SpringExtension" should {
      "have autowired the service" {
        service.repository.findUser().name shouldBe "system_user"
      }
    }
  }
}

@SpringBootTest를 사용하는 방법도 있다:

@SpringBootTest(classes = [FooService::class])
class SpringTest(fooService: FooService) : FunSpec() {
    override fun extensions() = listOf(SpringExtension)

    init {
        test("getFoo test") {
            val actual = fooService.getFoo()
            actual shouldBe Foo("id", "foo")
        }
    }
}

@Service
class FooService {
    fun getFoo(): Foo {
        return Foo("id", "foo")
    }
}

data class Foo(
    val id: String,
    val title: String,
)

테스트 컨텍스트

Spring 확장은 테스트가 실행되는 코루틴 컨텍스트를 통해 TestContextManager를 사용할 수 있게 해준다. 이 인스턴스에 대한 핸들은 testContextManager() 확장 메서드를 통해 얻을 수 있다.

이를 통해 Spring이 사용 중인 테스트 컨텍스트를 가져올 수 있다.

class MySpec(service: UserService) : WordSpec() {
  init {
    "SpringExtension" should {
      "provide the test context manager" {
         println("The context is " + testContextManager().testContext)
      }
    }
  }
}

테스트 메서드 콜백

Spring에는 테스트가 메서드라는 개념에 기반한 beforeTestMethod와 같은 다양한 테스트 콜백이 있다. 이 가정은 JUnit과 같은 레거시 테스트 프레임워크에는 적합하지만 테스트가 함수인 Kotest와 같은 최신 테스트 프레임워크에는 적용되지 않는다.

따라서 중첩된 스펙 스타일을 사용하는 경우 테스트 메서드 콜백이 실행되는 시기를 사용자 지정할 수 있다. 기본적으로 이것은 리프 노드에 있다. SpringTestLifecycleMode 인수를 확장에 전달하여 루트 노드에서 실행되도록 설정할 수 있다:

class ProjectConfig : AbstractProjectConfig() {
   override fun extensions() = listOf(SpringTestExtension(SpringTestLifecycleMode.Root))
}

파이널 클래스

파이널 클래스를 사용할 때 Kotest로부터 다음과 같은 경고를 받을 수 있다:

Using SpringListener on a final class. If any Spring annotation fails to work, try making this class open

원하는 경우에는 시스템 속성 kotest.listener.spring.ignore.warningtrue로 설정하여 이 경고를 비활성화할 수 있다.


참조




최종 수정 : 2024-04-23