如何解决Spock+MongoDB Testcontainer编写Spring Service集成测试的Bean注入错误?
问题描述
我有一个Spring Service类:
@Service public class MyService { @Autowired OtherService otherService; @Autowired MyRepository myRepository; // some methods... }
其中myRepository实现了MongoRepository。
我想用Groovy和Spock编写集成测试,要求:
otherService需要被MockmyRepository自动装配并使用Testcontainer的MongoDB
我写了如下测试代码:
@SpringBootTest(classes = [MyService.class, MyRepository.class]) @Testcontainers class MyServiceIntegrationTest extends Specification { @Shared static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:4.4.3") @Autowired MyRepository myRepository @SpringBean OtherService otherService = Mock() @Autowired MyService testedService @DynamicPropertySource static mongoProps(DynamicPropertyRegistry registry) { mongoDBContainer.start() registry.add("spring.data.mongodb.uri", () -> mongoDBContainer.getReplicaSetUrl()) } // some tests... }
但运行时报错:
Error creating bean with name 'myService': Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.xxx.xxx.MyRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
请问如何修改才能让测试正常运行?
问题分析与解决办法
这个报错的核心原因很明确:你在@SpringBootTest里只指定了MyService和MyRepository两个类,但Spring Data MongoDB要生成MongoRepository的代理实例,需要依赖一系列自动配置类(比如MongoDB连接配置、Repository代理生成逻辑等),仅把MyRepository加入测试配置,Spring根本不知道怎么创建它的实例。
给你两种可行的修改方案,按需选择:
方案一:手动引入MongoDB自动配置类
直接把MongoDB相关的核心自动配置类加到@SpringBootTest的classes参数中,让Spring能正确初始化MyRepository:
@SpringBootTest(classes = [MyService.class, MyRepository.class, MongoDataAutoConfiguration.class, MongoAutoConfiguration.class]) @Testcontainers class MyServiceIntegrationTest extends Specification { @Shared static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:4.4.3") @Autowired MyRepository myRepository @SpringBean OtherService otherService = Mock() @Autowired MyService testedService @DynamicPropertySource static mongoProps(DynamicPropertyRegistry registry) { // 划重点:不用手动调用start()!@Testcontainers会自动管理容器的启动和停止 registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl) } // some tests... }
方案二:加载主应用配置类(更推荐)
如果你不想手动拼凑配置类,最简单的方式是让测试加载项目中的主应用配置类(就是带有@SpringBootApplication的那个类)。这样Spring会自动引入所有必要的MongoDB配置,同时@SpringBean会自动替换掉容器中原有的OtherService为Mock实例,完美匹配你的需求:
// 把YourSpringBootApplication换成你项目实际的主应用类 @SpringBootTest(classes = YourSpringBootApplication.class) @Testcontainers class MyServiceIntegrationTest extends Specification { @Shared static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:4.4.3") @Autowired MyRepository myRepository @SpringBean OtherService otherService = Mock() @Autowired MyService testedService @DynamicPropertySource static mongoProps(DynamicPropertyRegistry registry) { registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl) } // some tests... }
这种方式更简洁,符合Spring Boot的自动配置设计理念,后续维护成本也更低。
额外提醒
- 确保
MyRepository所在的包能被Spring扫描到:要么它在主应用类的子包下,要么你在主应用类上用@EnableMongoRepositories指定了Repository的扫描路径。 - 建议把
MyService的字段注入改成构造函数注入,这是Spring官方推荐的方式,测试时的依赖注入逻辑会更稳定。
内容的提问来源于stack exchange,提问作者Wiktor
相关产品推荐
相关产品推荐

