【发布时间】:2021-12-28 18:39:35
【问题描述】:
我的控制器调用该服务来发布有关汽车的信息,如下所示,它工作正常。但是,我的单元测试因 IllegalArgumentException: URI is not absolute 异常而失败,并且 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 is not absolute 异常:
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 is not absolute”执行。这是因为在我上面的carService 中,我使用@Value 从application.properties 文件中注入uri,但是在单元测试中,并没有注入。
所以,我添加了公共属性以便能够设置它并更新上面的代码,但后来我发现uri 必须是 real uri 到 real 后端,需要一个真实的数据库。
换句话说,如果我传递的 uri 是假 uri,那么上面对 carServiceSut.getCar 的调用将失败,这意味着这会将测试变成集成测试。
这与在单元测试中使用模拟相矛盾。
我不想调用真正的后端,restTemplateMock 应该被模拟并注入carServiceSut,因为它们分别被注释为@Mock 和@InjectMock。因此,它应该是一个单元测试并被隔离,而不需要调用真正的后端。我感觉 Mockito 和 RestTemplate 不能很好地协同工作。
【问题讨论】:
-
你能发布堆栈跟踪吗?我的猜测 - 被测系统中的 null uri
-
是的,所以我为 URI 创建了 setter;但是,这应该是单元测试,而不是我发现的集成测试。因此,上面对
carServiceSut.getCar的调用需要真正的uri、真正的端点、真正的身份验证、真正的数据库,这是非常糟糕的。我想要这个嘲笑,看起来 mockito 和 restTemplate 不能正常工作。我不想调用一个真正的端点,我希望这一切都被嘲笑
标签: spring-boot unit-testing mockito resttemplate