RESTTemplate.postForEntity的Spring Mockito测试引发IllegalArgumentException:URI不是绝对的 [英] Spring Mockito test of RestTemplate.postForEntity throws IllegalArgumentException: URI is not absolute
问题描述
我的控制器调用该服务来发布有关汽车的信息,如下所示,它运行良好。但是,我的单元测试失败,出现IlLegalArgumentException:URI不是绝对异常异常,SO上的所有帖子都无法帮助解决此问题。
这是我的控制器
@RestController
@RequestMapping("/cars")
public class CarController {
@Autowired
CarService carService;
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<CarResponse> getCar(@RequestBody CarRequest carRequest, @RequestHeader HttpHeaders httpHeaders) {
ResponseEntity<CarResponse> carResponse = carService.getCard(carRequest, httpHeaders);
return carResponse;
}
}
这是我的服务类:
@Service
public class MyServiceImpl implements MyService {
@Value("${myUri}")
private String uri;
public void setUri(String uri) { this.uri = uri; }
@Override
public ResponseEntity<CarResponse> postCar(CarRequest carRequest, HttpHeaders httpHeaders) {
List<String> authHeader = httpHeaders.get("authorization");
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", authHeader.get(0));
HttpEntity<CarRequest> request = new HttpEntity<CarRequest>(carRequest, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<CarResponse> carResponse = restTemplate.postForEntity(uri, request, CarResponse.class);
return cardResponse;
}
}
然而,我在执行单元测试时遇到了问题。以下测试引发IlLegalArgumentException:URI不是绝对异常异常:
public class CarServiceTest {
@InjectMocks
CarServiceImpl carServiceSut;
@Mock
RestTemplate restTemplateMock;
CardResponse cardResponseFake = new CardResponse();
@BeforeEach
void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
cardResponseFake.setCarVin(12345);
}
@Test
final void test_GetCars() {
// Arrange
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", anyString());
ResponseEntity<CarResponse> carResponseEntity = new ResponseEntity(carResponseFake, HttpStatus.OK);
String uri = "http://FAKE/URI/myapi/cars";
carServiceSut.setUri(uri);
when(restTemplateMock.postForEntity(
eq(uri),
Mockito.<HttpEntity<CarRequest>> any(),
Mockito.<Class<CarResponse>> any()))
.thenReturn(carResponseEntity);
// Act
**// NOTE: Calling this requires real uri, real authentication,
// real database which is contradicting with mocking and makes
// this an integration test rather than unit test.**
ResponseEntity<CarResponse> carResponseMock = carServiceSut.getCar(carRequestFake, headers);
// Assert
assertEquals(carResponseEntity.getBody().getCarVin(), 12345);
}
}
更新%1
我弄明白了为什么URI不是绝对的&引发了执行。这是因为在我上面carService
中,我使用@Value
从application.properties
文件中注入uri
,但在单元测试中,没有注入。
因此,我添加了公共属性以便能够设置它,并更新了上面的代码,但随后我发现uri
必须是指向实际后端的实际URI,这需要一个实际数据库。
换句话说,如果uri
传递的uri
是假的URI,则对上面的carServiceSut.getCar
的调用将失败,这意味着这将把测试变成集成测试。
这与在单元测试中使用模拟相矛盾。
我不想调用真正的后台,restTemplateMock
应该被模仿并注入到carServiceSut
中,因为它们分别被注释为@Mock
和@InjectMock
。因此,它应该停留在单元测试并被隔离,而不需要调用真正的后端。我有一种感觉,Mockito和RestTemplate不能很好地配合。
推荐答案
您需要正确构建被测系统。
当前MyServiceImpl.uri
为空。
更重要的是,您的RestTemplate模拟没有注入到任何地方,并且您在测试的方法中构造了一个新的RestTemplate。
由于Mockito不支持部分注入,您需要在测试中手动构建实例。
我会:
使用构造函数注入同时注入rest模板和uri:
@Service
public class MyServiceImpl implements MyService {
private RestTemplate restTemplate;
private String uri;
public MyServiceImpl(RestTemplate restTemplate, @Value("${myUri}") uri) {
this.restTemplate = restTemplate;
this.uri = uri;
}
手动构建实例:
- 删除@Mock和@InjectMock
- 删除Mockito.initMock调用
- 在测试中使用Mockito.mock和构造函数
public class CarServiceTest {
public static String TEST_URI = "YOUR_URI";
RestTemplate restTemplateMock = Mockito.mock(RestTemplate.class);
CarServiceImpl carServiceSut = new CarServiceImpl(restTemplateMock, TEST_URI):
}
删除测试方法中restTemplate
的创建。
如果需要,添加一个提供RestTemplate Bean的配置类(对于应用程序,测试不需要这样做):
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
注意RestTemplate是线程安全的,每个应用程序一个实例就足够了:Is RestTemplate thread safe?
这篇关于RESTTemplate.postForEntity的Spring Mockito测试引发IllegalArgumentException:URI不是绝对的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!