You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin Spring Boot集成Test Containers报ParameterResolutionException求助

Kotlin Spring Boot集成TestContainers启动失败问题排查

异常信息

org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [bootiful.kotlin.CustomerRepository customerRepository] in constructor [public bootiful.kotlin.KotlinApplicationTests(bootiful.kotlin.CustomerRepository)]: Failed to load ApplicationContext for [ReactiveWebMergedContextConfiguration@55fb36de testClass = bootiful.kotlin.KotlinApplicationTests, locations = [], classes = [bootiful.kotlin.KotlinApplication, bootiful.kotlin.CustomerRepository], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.testcontainers.service.connection.ServiceConnectionContextCustomizer@0, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@a4ca3f6, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@b6b1987, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7c541c15, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@3f0846c6, org.springframework.boot.test.web.reactive.server.WebTestClientContextCustomizer@3e10dc6, org.springframework.boot.test.context.SpringBootTestAnnotation@5de402fc], contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
    at org.junit.jupiter.engine.execution.ParameterResolutionUtils.resolveParameter(ParameterResolutionUtils.java:159)
    ...(省略部分栈信息)
Caused by: java.lang.IllegalStateException: Failed to load ApplicationContext for [ReactiveWebMergedContextConfiguration@55fb36de testClass = bootiful.kotlin.KotlinApplicationTests, locations = [], classes = [bootiful.kotlin.KotlinApplication, bootiful.kotlin.CustomerRepository], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.testcontainers.service.connection.ServiceConnectionContextCustomizer@0, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@a4ca3f6, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@b6b1987, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7c541c15, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@3f0846c6, org.springframework.boot.test.web.reactive.server.WebTestClientContextCustomizer@3e10dc6, org.springframework.boot.test.context.SpringBootTestAnnotation@5de402fc], contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]

相关配置与代码

主目录application.properties

spring.r2dbc.url=r2dbc:postgresql://localhost/postgres
spring.r2dbc.username=postgres
spring.r2dbc.password=postgres
spring.sql.init.mode=always

测试目录application.properties

spring.sql.init.mode=always
spring.r2dbc.url=r2dbc:tc:postgresql:///customers?TC_IMAGE_TAG=13.3

测试类代码

@SpringBootTest
class KotlinApplicationTests(@Autowired val customerRepository: CustomerRepository) {

    @Test
    fun contextLoads() {
        runBlocking {
            customerRepository.save(Customer(null, "Hadi"))
            val customers = customerRepository.findAll()
            Assertions.assertNotNull(customers.last().id)
            Assertions.assertEquals(customers.count(), 6)
        }
    }
}

TestContainers相关代码

class TestKotlinApplication {
    @Bean
    @ServiceConnection
    fun postgresContainer(): PostgreSQLContainer<*> {
        return PostgreSQLContainer("postgres:latest")
    }
}
fun main(args: Array<String>) {
    fromApplication<KotlinApplication>().with(TestKotlinApplication::class).run(*args)
}

主应用类代码

@SpringBootApplication
class KotlinApplication
fun main(args: Array<String>) {
    runApplication<KotlinApplication>(*args)
}
interface CustomerRepository:CoroutineCrudRepository<Customer,Int>{}
data class Customer(@Id val id: Int?, val name:String){}

build.gradle.kts配置

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
    id("org.springframework.boot") version "3.1.2"
    id("io.spring.dependency-management") version "1.1.2"
    id("org.graalvm.buildtools.native") version "0.9.23"
    kotlin("jvm") version "1.8.20"
    kotlin("plugin.spring") version "1.8.20"
}
group = "bootiful"
version = "0.0.1-SNAPSHOT"
java {
    sourceCompatibility = JavaVersion.VERSION_17
}
repositories {
    mavenCentral()
}
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
    runtimeOnly("org.postgresql:postgresql")
    runtimeOnly("org.postgresql:r2dbc-postgresql")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.boot:spring-boot-testcontainers")
    testImplementation("io.projectreactor:reactor-test")
    testImplementation("org.testcontainers:junit-jupiter")
    testImplementation("org.testcontainers:postgresql")
    testImplementation("org.testcontainers:r2dbc")
}
tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs += "-Xjsr305=strict"
        jvmTarget = "17"
    }
}
tasks.withType<Test> {
    useJUnitPlatform()
}

错误原因分析

  1. TestContainers配置冲突:同时使用了两种TestContainers集成方式——r2dbc:tc: URL协议自动启动容器,以及手动定义@ServiceConnection的PostgreSQLContainer Bean,两种方式会导致Spring无法正确解析数据库连接配置,进而无法初始化ApplicationContext。
  2. 测试上下文未加载Test配置类:TestKotlinApplication类中的容器Bean未被测试上下文扫描到,即使没有冲突,也无法正常启动容器,但核心问题还是配置冲突。

解决方案

方案一:使用r2dbc:tc: URL协议(推荐,更简洁)

  • 删除TestKotlinApplication类及其主方法,无需手动定义容器Bean,r2dbc:tc:协议会自动启动对应版本的PostgreSQL容器。
  • 保留测试目录application.properties中的配置:
    spring.sql.init.mode=always
    spring.r2dbc.url=r2dbc:tc:postgresql:///customers?TC_IMAGE_TAG=13.3
    
  • 测试类保持不变,构造器注入CustomerRepository是合法的(Spring Boot Test支持构造器自动注入,无需@Autowired也可,保留也没问题)。

方案二:使用@ServiceConnection Bean方式

  • 删除测试目录application.properties中的spring.r2dbc.url配置,@ServiceConnection会自动将容器的连接信息绑定到Spring的R2DBC配置中。
  • 修改测试类,确保TestKotlinApplication被测试上下文加载:
    @SpringBootTest(classes = [KotlinApplication::class, TestKotlinApplication::class])
    class KotlinApplicationTests(val customerRepository: CustomerRepository) {
        // 测试方法不变
    }
    
  • 建议将PostgreSQLContainer的镜像版本改为postgres:13.3,与之前URL中的版本一致,避免版本差异问题:
    @Bean
    @ServiceConnection
    fun postgresContainer(): PostgreSQLContainer<*> {
        return PostgreSQLContainer("postgres:13.3")
    }
    

验证

修改后运行测试,Spring会正确启动TestContainers PostgreSQL容器,初始化数据库连接,ApplicationContext正常加载,测试即可执行。

内容的提问来源于stack exchange,提问作者Edgar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 09:57:02