RESTTemplate.postForEntity的Spring Mockito测试引发IllegalArgumentException:URI不是绝对的 [英] Spring Mockito test of RestTemplate.postForEntity throws IllegalArgumentException: URI is not absolute

查看:0
本文介绍了RESTTemplate.postForEntity的Spring Mockito测试引发IllegalArgumentException:URI不是绝对的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的控制器调用该服务来发布有关汽车的信息,如下所示,它运行良好。但是,我的单元测试失败,出现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中,我使用@Valueapplication.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆