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

Testcontainers集成MSSQL测试时@Autowired注入Repository失败

问题描述

使用Testcontainers基于MSSQL Server镜像开展Spring Data JPA集成测试时,注入Repository抛出如下错误:

lateinit property modelRepository has not been initialized

相同测试配置在PostgreSQL项目中可正常运行。

相关代码

测试类

package *.models.integration

import *.portadapter.repository.ModelRepository
import *.builders.ModelBuilder
import org.junit.ClassRule
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.MSSQLServerContainer
import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy
import org.testcontainers.junit.jupiter.Testcontainers
import java.time.Duration
import java.util.function.Supplier

@DataJpaTest
@Testcontainers
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ModelRepositoryTest {

    @Autowired
    private lateinit var modelRepository: ModelRepository

    companion object {
        private val hibernateDriver: Supplier<Any> = Supplier { "org.hibernate.dialect.SQLServerDialect" }
        private val hibernateDll: Supplier<Any> = Supplier { "none" }

        @ClassRule
        @JvmField
        val mssqlserver: MSSQLServerContainer<*> = MSSQLServerContainer("mcr.microsoft.com/mssql/server:2017-latest")
            .withStartupCheckStrategy(MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(5)))
            .acceptLicense()

        @JvmStatic
        @DynamicPropertySource
        fun properties(registry: DynamicPropertyRegistry) {
            registry.add("spring.datasource.url", mssqlserver::getJdbcUrl)
            registry.add("spring.datasource.username", mssqlserver::getUsername)
            registry.add("spring.datasource.password", mssqlserver::getPassword)
            registry.add("spring.jpa.properties.hibernate.dialect", hibernateDriver)
            registry.add("spring.jpa.hibernate.ddl-auto", hibernateDll)

            mssqlserver.acceptLicense().start()
        }
    }

    @Test
    fun `it should save a model`() {
        val model = ModelBuilder().createModel().get()
        modelRepository.save(model)

        val modelFound = modelRepository.findByCode(model.code)

        assertEquals(model.code, modelFound.get().code)
    }
}

Repository类

package *.portadapter.repository

import *.model.domain.Model
import org.springframework.data.jpa.repository.JpaRepository
import java.util.*

interface ModelRepository: JpaRepository<Model, String> {
    fun findByCode(code: String): Optional<Model>
}
问题根因

一共三个核心问题:

  • 包扫描路径不匹配:@DataJpaTest默认仅扫描测试类所在包及其子包下的Spring Data Repository、实体类。当前测试类在*.models.integration包,而ModelRepository在*.portadapter.repository包,不在默认扫描范围内,Spring上下文未生成Repository对应的Bean,是注入失败的直接原因。PostgreSQL场景可正常运行,大概率是对应项目中测试类与Repository包路径符合默认扫描规则,或手动配置过扫描路径。
  • JUnit版本注解混用:代码中同时混用JUnit4和JUnit5(Jupiter)的注解:@ClassRule、org.junit.Test属于JUnit4组件,@Testcontainers、@DynamicPropertySource属于JUnit5组件。混用会导致容器生命周期管理、属性注入逻辑异常,引发上下文启动不稳定。
  • 冗余配置错误:@DynamicPropertySource标注的方法中手动调用mssqlserver.acceptLicense().start()属于重复逻辑,Testcontainers会自动管理容器生命周期,手动启动可能引发端口冲突、重复启动等问题;另外spring.jpa.hibernate.ddl-auto设为none会导致测试时不会自动创建表结构,后续执行数据库操作会报表不存在错误。
修复方案

按以下步骤调整代码即可:

  1. 配置@DataJpaTest的扫描路径,确保Spring能找到Repository和实体类,替换类上的@DataJpaTest注解为:
    @DataJpaTest(basePackages = ["*.portadapter.repository"])
    @EntityScan(basePackages = ["*.model.domain"])
    
    注意将包路径替换为项目实际的Repository、实体类根包路径。
  2. 统一使用JUnit5注解,移除JUnit4相关配置:
    • 删除import org.junit.Test,替换为import org.junit.jupiter.api.Test
    • 删除容器字段上的@ClassRule、@JvmField注解,替换为Testcontainers for JUnit5的@Container注解
  3. 移除冗余配置,修正Hibernate DDL配置:
    • 删除properties方法中mssqlserver.acceptLicense().start()这行冗余调用
    • 将spring.jpa.hibernate.ddl-auto配置改为create-drop,测试时自动建表、测试结束自动删表,不残留测试数据

修复后完整测试类参考

package *.models.integration

import *.portadapter.repository.ModelRepository
import *.builders.ModelBuilder
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.MSSQLServerContainer
import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import java.time.Duration

@DataJpaTest(basePackages = ["*.portadapter.repository"])
@EntityScan(basePackages = ["*.model.domain"])
@Testcontainers
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ModelRepositoryTest {

    @Autowired
    private lateinit var modelRepository: ModelRepository

    companion object {
        @Container
        @JvmField
        val mssqlserver: MSSQLServerContainer<*> = MSSQLServerContainer("mcr.microsoft.com/mssql/server:2017-latest")
            .withStartupCheckStrategy(MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(5)))
            .acceptLicense()

        @JvmStatic
        @DynamicPropertySource
        fun properties(registry: DynamicPropertyRegistry) {
            registry.add("spring.datasource.url", mssqlserver::getJdbcUrl)
            registry.add("spring.datasource.username", mssqlserver::getUsername)
            registry.add("spring.datasource.password", mssqlserver::getPassword)
            registry.add("spring.jpa.properties.hibernate.dialect") { "org.hibernate.dialect.SQLServerDialect" }
            registry.add("spring.jpa.hibernate.ddl-auto") { "create-drop" }
        }
    }

    @Test
    fun `it should save a model`() {
        val model = ModelBuilder().createModel().get()
        modelRepository.save(model)

        val modelFound = modelRepository.findByCode(model.code)

        assertEquals(model.code, modelFound.get().code)
    }
}

内容的提问来源于stack exchange,提问作者Carlos Eduardo Ilgenfritz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:15:34