Mockito返回“java.lang.非法参数异常:URI不是绝对的”在RestTemplate.exchangeSpringboot

mjqavswn  于 2023-01-30  发布在  Spring
关注(0)|答案(2)|浏览(501)

Mockito返回"java. lang.非法参数异常:URI不是绝对的"。我不知道为什么会发生这种情况,因为我似乎正确地模拟了restTemplate,而且因为我看到了这个异常,所以似乎restTemplate不是一个模拟。
这是我的班级

@Component
public class RestTemplateWrapper {
  private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateWrapper.class);
  
  public <T> ResponseEntity<T> callWebServiceGET(String url,HttpEntity<?> httpEntity,
      ParameterizedTypeReference<T> parameterizedTypeReference) {

    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<T> response=null;
    LOGGER.trace("Entered callWebServiceGET");
    LOGGER.info("Calling WebService {}", url);
    try {
      response=restTemplate.exchange(url, HttpMethod.GET, httpEntity, parameterizedTypeReference);
    } catch (HttpClientErrorException e) {
      if (HttpStatus.NOT_FOUND.equals(e.getStatusCode())) {
        LOGGER.error("Service Unavailable - Code 404 returned. " + url + e.getMessage());
      } else if (HttpStatus.UNAUTHORIZED.equals(e.getStatusCode())) {
        LOGGER.error("Token Expired- Code 401 returned. " + e.getMessage());
      } else if (HttpStatus.BAD_REQUEST.equals(e.getStatusCode())) {
        LOGGER.error("Bad Input, 400 returned.{} {} ", url , e.getMessage(), e);
      } else {
        LOGGER.error("WEB Service Failure. " + e.getMessage());
      }
    }
    return response;
  }

}

下面是我的测试用例:

@PrepareForTest({RestTemplateWrapper.class})
public class RestTemplateWrapperTest  {
  
  @Mock
  private RestTemplate mockRestTemplate;
  @InjectMocks
  private RestTemplateWrapper webUtils;
  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
  }
  @Test
  public void callWebServiceGET_OK()  {
    HttpEntity<String> httpEntity= new ResponseEntity<>(HttpStatus.OK);
    ResponseEntity<String> entityResponse=new ResponseEntity<>("MOCK_RESPONSE", HttpStatus.OK);
    when(mockRestTemplate.exchange(eq("/objects/get-objectA"), eq(HttpMethod.GET), eq(httpEntity),any(
        ParameterizedTypeReference.class))).thenReturn(
        entityResponse);
    ResponseEntity<String> mockResponse= webUtils.callWebServiceGET("",null,  new ParameterizedTypeReference<String>(){
    });
    //verify(mockRestTemplate,times(1)).exchange(Matchers.anyString(), Matchers.any(), Matchers.any());
     Assert.assertEquals("MOCK_RESPONSE",mockResponse.getBody());
  }
  
}

答复:

URI is not absolute
java.lang.IllegalArgumentException: URI is not absolute
    at java.net.URI.toURL(URI.java:1088)
    at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:145)
    at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:87)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:727)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:666)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:604)
    at com.project.di.tp.purchaseorderservice.utils.RestTemplateWrapper.callWebServiceGET(RestTemplateWrapper.java:29)
    at com.project.di.tp.purchaseorderservice.utils.RestTemplateWrapperTest.callWebServiceGET_OK(RestTemplateWrapperTest.java:51)

有什么办法解决这个问题吗?我已经试了4个小时了。

wztqucjr

wztqucjr1#

我找到了解决方法,问题似乎是我的类RestTemplateWrapper在callWebServiceGET内创建了一个示例,因此mockito不能模拟那个对象。如果在方法外设置对象,它可以工作,但我不想这样做。
有没有办法模仿一个方法中的对象?

qxsslcnc

qxsslcnc2#

尽管JavaDocs中没有明确说明,但您必须在其中提供一个绝对URL。
这是因为您没有提供相对URL相对于的基本URL。您也不能在浏览器中输入"/objects/get-objectA"作为URL。
因此我建议您使用类似"http://example.com/objects/get-objectA"的内容来代替第一个参数:

when(mockRestTemplate.exchange(
        eq("http://example.com/bla"), 
        eq(HttpMethod.GET), 
        isNull(HttpEntity.class), 
        any(ParameterizedTypeReference.class))).
    thenReturn(entityResponse);

ResponseEntity<String> mockResponse =
    webUtils.callWebServiceGET(
        "http://example.com/bla", 
        null, 
        new ParameterizedTypeReference<String>(){});

请注意,使用给定参数调用webUtils.callWebServiceGET不会使Mockito返回想要的答案,因此我将调用中的URL更改为您在Mockito.when中期望的绝对URL,还更改了期望有类型化null(类型化以匹配方法签名)的参数。

更新:

正如您自己已经发现的那样,Mockito.when不起作用,因为您没有在tested方法中使用从测试中创建的mock,而是在callWebServiceGET的每次调用中创建RestTemplate的一个新示例(不知道为什么我之前没有看到它,抱歉!)
我建议您使用构造函数将RestTemplate注入到测试类中:

private final RestTemplate restTemplate;
public RestTemplateWrapper(RestTemplate restTemplate) {
  this.restTemplate = restTemplate;
}

// remove the following line in the method callWebServiceGET:
// RestTemplate restTemplate = new RestTemplate();

使用这段代码,Spring会自动将模拟的RestTemplate注入到测试中,但是要运行生产代码,您需要添加一个bean来提供用于注入的RestTemplate
将其添加到Configuration类中,您还可以在其中定义其他Bean:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
   // Do any additional configuration here
   return builder.build();
}

(在How to autowire RestTemplate using annotations的答案中找到此代码片段)
作为测试的一般建议:尽量避免在你想要测试的代码中使用new操作符,而使用注入来代替。如果你需要创建多个示例(例如在一个循环中),尝试注入一个为你创建示例的工厂-这样在测试中你就可以模拟工厂。

相关问题