JUnit测试RestTemplate时Mock失效连接真实URL问题求助
问题分析
你的测试代码Mock了RestTemplate但未生效,核心原因是业务类TestRestTemplate的RestTemplate是在自身构造函数中直接new出来的,测试类中@Mock的实例和业务类里的实例完全独立,Mock配置无法覆盖真实实例,代码依然会发起实际HTTP请求,导致连接超时。
解决方案
以下两种方案均可解决问题,核心思路是让业务类的RestTemplate实例支持外部注入,测试时传入Mock对象。
方案1:构造函数注入RestTemplate
修改业务类,通过构造函数传入RestTemplate,同时保留无参构造函数兼容原有运行方式:
public class TestRestTemplate { private final RestTemplate restTemplate; // 带参数构造函数,用于测试注入Mock实例 public TestRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; } // 无参构造函数,保持原有独立程序运行逻辑 public TestRestTemplate() { this(new RestTemplate()); } public void identifyInvoiceCategory(final Properties properties) throws Throwable { final ResponseEntity<SearchResponse> exchange = restTemplate.exchange( properties.getProperty("ENDPOINT"), HttpMethod.GET, new HttpEntity<>(createSearchRequest(), createHttpHeader()), SearchResponse.class); final SearchResponse searchResponse = exchange.getBody(); searchResponse.getEndIndex(); } private SearchRequest createSearchRequest() throws Throwable { final SearchRequest searchRequest = new SearchRequest(); return searchRequest; } private HttpHeaders createHttpHeader() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); return httpHeaders; } }
修改测试类,传入Mock的RestTemplate并构造有效响应(避免getBody()时NPE):
@RunWith(MockitoJUnitRunner.class) public class TestRest { @Mock private RestTemplate restTemplate; @Test public void testIdentifyInvoiceCategoryValid() throws Throwable { // 构造Mock响应数据 SearchResponse mockResponse = new SearchResponse(); mockResponse.setEndIndex(10); // 根据实际类的属性设置有效值 ResponseEntity<SearchResponse> responseEntity = new ResponseEntity<>(mockResponse, HttpStatus.OK); // 配置Mock行为 Mockito.when(restTemplate.exchange( ArgumentMatchers.anyString(), ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.any(HttpEntity.class), ArgumentMatchers.<Class<SearchResponse>>any()) ).thenReturn(responseEntity); // 传入Mock实例创建业务类对象 TestRestTemplate testInstance = new TestRestTemplate(restTemplate); testInstance.identifyInvoiceCategory(createProperties()); } private Properties createProperties() { final Properties properties = new Properties(); properties.setProperty("ENDPOINT", "http://www.test.com"); return properties; } }
方案2:Setter方法注入RestTemplate
如果不想修改构造函数,可添加Setter方法实现注入:
业务类添加Setter:
public class TestRestTemplate { private RestTemplate restTemplate; public TestRestTemplate() { restTemplate = new RestTemplate(); } // 添加Setter用于测试注入Mock实例 public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; } // 其余业务方法不变... }
测试类修改为通过Setter注入Mock:
@RunWith(MockitoJUnitRunner.class) public class TestRest { @Mock private RestTemplate restTemplate; @Test public void testIdentifyInvoiceCategoryValid() throws Throwable { // 构造Mock响应数据 SearchResponse mockResponse = new SearchResponse(); mockResponse.setEndIndex(10); ResponseEntity<SearchResponse> responseEntity = new ResponseEntity<>(mockResponse, HttpStatus.OK); // 配置Mock行为 Mockito.when(restTemplate.exchange( ArgumentMatchers.anyString(), ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.any(HttpEntity.class), ArgumentMatchers.<Class<SearchResponse>>any()) ).thenReturn(responseEntity); TestRestTemplate testInstance = new TestRestTemplate(); // 注入Mock实例 testInstance.setRestTemplate(restTemplate); testInstance.identifyInvoiceCategory(createProperties()); } private Properties createProperties() { final Properties properties = new Properties(); properties.setProperty("ENDPOINT", "http://www.test.com"); return properties; } }
内容的提问来源于stack exchange,提问作者user1862354
相关产品推荐
相关产品推荐

