使用@RestClientTest单元测试时排除@SpringBootApplication类
我完全懂你的困扰——用@RestClientTest测试MyServiceImpl时,Spring默认会加载你的@SpringBootApplication类(也就是DemoApplication),导致里面定义的xyzService也被初始化,而它依赖的RabbitTemplate在测试环境不存在,直接报错。你只想初始化测试需要的Bean,不想用Mock污染代码,对吧?
这里有两种干净的解决方案:
方案一:拆分配置类(推荐)
把DemoApplication里的Bean按职责拆分,将和RestTemplate相关的配置单独放到一个配置类里,和业务Bean(比如xyzService)分离。这样测试时只加载需要的配置即可。
1. 新建RestTemplate配置类
@Configuration class RestTemplateConfig { @Bean fun restTemplate() = RestTemplateBuilder().build() }
2. 修改DemoApplication
只保留业务相关的Bean定义:
@SpringBootApplication class DemoApplication { @Bean fun xyzService() = XyzService() }
3. 修改测试类
用@Import导入RestTemplateConfig,同时@RestClientTest只指定要测试的MyServiceImpl:
@RestClientTest(MyServiceImpl::class) @Import(RestTemplateConfig::class) @AutoConfigureWebClient(registerRestTemplate = true) class MyServiceTest { @Autowired private lateinit var mockServer: MockRestServiceServer @Autowired private lateinit var myService: MyService @Test fun test(){ mockServer.expect(ExpectedCount.once(), MockRestRequestMatchers.requestTo(URI("http://localhost:9090"))) .andExpect(MockRestRequestMatchers.method(HttpMethod.GET)) .andRespond(MockRestResponseCreators.withStatus(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON) .body("hello world")) myService.fetch("123") } }
这样Spring只会加载MyServiceImpl和RestTemplateConfig里的Bean,完全不会碰xyzService,也就避免了初始化失败的问题。
方案二:指定测试上下文的配置类
如果不想拆分配置类,可以直接在测试类里用@ContextConfiguration指定只加载需要的类,跳过DemoApplication的扫描:
@RestClientTest(MyServiceImpl::class) @ContextConfiguration(classes = [MyServiceImpl::class, RestTemplateConfig::class]) @AutoConfigureWebClient(registerRestTemplate = true) class MyServiceTest { // 测试代码不变 }
这个方法直接告诉Spring测试上下文只加载指定的类,不会自动加载@SpringBootApplication里的所有Bean,同样能解决问题。
为什么原来的代码会出问题?
@RestClientTest是一个组合注解,它默认会自动检测项目中的@SpringBootApplication或@Configuration类作为测试上下文的配置源。所以你的DemoApplication里的所有Bean定义都会被执行,包括依赖RabbitTemplate的xyzService,而测试环境没有配置RabbitMQ相关的Bean,自然就初始化失败了。
内容的提问来源于stack exchange,提问作者sidgate

