Spring Boot JUnit测试失败:RestTemplate模拟无效致连接错误
问题分析与修复方案
核心问题梳理
- 依赖类型不匹配:PromoClient依赖的是
RestTemplate,但测试里Mock的是TestRestTemplate,两者不属于同一类型,导致Spring容器无法将Mock实例注入到PromoClient中,最终还是使用真实的RestTemplate发起请求。 - Mock未被Spring容器管理:使用Mockito的
@Mock注解而非Spring Boot的@MockBean,导致Mock实例不会被注册到Spring上下文,PromoClient自然无法获取到Mock对象。 - 自定义Bean覆盖Mock逻辑:测试类中定义了返回
TestRestTemplate的@Bean,这会直接覆盖容器中的RestTemplate实例,且这是真实的HTTP客户端,会发起真实网络请求。 - 配置未注入测试值:
billingUrl未在测试中指定,导致使用了默认配置值(如报错中的http://localhost:8080/v1/pr),触发真实请求。
修复后的测试代码
import com.webapp.util.PromotionsDTO import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.`when` import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.test.context.TestConstructor import org.springframework.web.client.RestTemplate @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.MOCK, properties = ["billing.url=https://example.com/promotions"] ) @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) class PromoClientTest { @Autowired private lateinit var promoClient: PromoClient // 使用@MockBean将RestTemplate的Mock实例注册到Spring上下文 @MockBean private lateinit var restTemplate: RestTemplate @Test fun testGetPromotion() { val expectedPromotion = PromotionsDTO("TEST PROMO") // 匹配PromoClient中调用的getForEntity重载方法 `when`(restTemplate.getForEntity(anyString(), PromotionsDTO::class.java)) .thenReturn(ResponseEntity(expectedPromotion, HttpStatus.OK)) val result = promoClient.getPromotion() assertEquals(expectedPromotion, result) } }
额外优化建议
如果不需要启动完整Spring上下文,可改用单元测试模式,避免集成测试的开销:
import com.webapp.util.PromotionsDTO import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.junit.jupiter.MockitoExtension import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.client.RestTemplate @ExtendWith(MockitoExtension::class) class PromoClientUnitTest { @Mock private lateinit var restTemplate: RestTemplate @InjectMocks private lateinit var promoClient: PromoClient @Test fun testGetPromotion() { val billingUrl = "https://example.com/promotions" // 手动设置billingUrl(需将原类中billingUrl改为internal或添加setter) promoClient.billingUrl = billingUrl val expectedPromotion = PromotionsDTO("TEST PROMO") `when`(restTemplate.getForEntity(billingUrl, PromotionsDTO::class.java)) .thenReturn(ResponseEntity(expectedPromotion, HttpStatus.OK)) val result = promoClient.getPromotion() assertEquals(expectedPromotion, result) } }
内容的提问来源于stack exchange,提问作者user0123812
相关产品推荐
相关产品推荐

