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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 03:17:45