I want to mock the postForEntity method of a RestTemplate instance variable belonging to one of my classes, but it keeps failing with a "URI is not absolute" error despite my seeing anyString() being passed in as the URI in other examples. Something that should be noted is that I did not @Mock my RestTemplate, because I want to test if it's been correctly built by the constructor of its parent class (namely the timeout).
I can't post actual code but the class I'm testing looks something like this:
public class MyClient {
...
@Getter
@Setter
private RestTemplate restTemplate = new RestTemplate();
public MyClient(Property x, Property y, Property z){
this.x = x;
this.y = y;
this.z = z;
}
//New constructor that I want to test
public MyClient(Property x, Property y, Property z, int timeout){
this(x, y, z);
this.restTemplate =((SimpleClientHttpRequestFactory)getRestTemplate().getRequestFactory()).setReadTimeout(timeout);
}
public makeRequests() {
...
//myEndpoint, myRequest are assigned earlier in this fx, CustomResponse is a custom class
ResponseEntity resEntity = getRestTemplate().postForEntity(myEndpoint, myRequest, CustomResponse.class);
}
}
I want to test whether MyClient.restTemplate is timing out:
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest extends TestCase{
...
@Test
public void test_new_constructor(){
MyClient myClient = new MyClient(x, y, z, 1000);
RestTemplate restTemplate = myClient.getRestTemplate();
Mockito.when(restTemplate.postForEntity(anyString(), any(), any(Class.class))).thenAnswer(new AnswersWithDelay(100000, new Returns(new ResponseEntity(CustomResponse, HttpStatus.OK)) ));
//Will eventually add a delay to trigger a timeout + code for checking the exception, but it doesn't make it past the above line
myClient.makeRequests();
}
}
It fails on the Mockito.when(restTemplate.postForEntity(anyString(), any(), any(Class.class))).thenAnswer(new AnswersWithDelay(100000, new Returns(new ResponseEntity(CustomResponse, HttpStatus.OK)) ));
line with java.lang.IllegalArgumentException: URI is not absolute
, why is this? Is it a bad idea to test an actual RestTemplate in the first place? I was thinking that using Mockito.when
on the network calls would make it so that the tests aren't reliant on requests to actual endpoints, but is this not the case?
If you haven't mocked an instance, you cannot call when
with it. when
is reserved for mock objects or spies.
The line Mockito.when(restTemplate.postForEntity(anyString(), any(), any(Class.class)))
calls the real postForEntity
method with parameters anyString(), any(), any(Class.class)
, which is really just "", null, null
:
public static String anyString() {
reportMatcher(new InstanceOf(String.class, "<any string>"));
return "";
}
#any
:
public static <T> T any() {
reportMatcher(Any.ANY);
return null;
}
The empty string ""
obviously isn't an absolute URI.
If you want to test a class, you must not mock it; otherwise you would only be testing the mock object/mock behavior and not your real class.
That said, I think it is fair to assume that RestTemplate
has already been thoroughly tested by the framework authors – why do you want to test if it detects/triggers timeouts correctly? That should already be covered by the tests for RestTemplate
.
If, however, you want to test if your application code handles timeouts of RestTemplate
correctly, then by all means create a mock instance of RestTemplate
, set it up to behave like it timeouted, and verify that your own code handles the timeout of the template correctly.