Reputation: 35
Given a class EncoderService
which has the following createNewStream
method and a bunch of constants used in the method, how can I use mockito
to write a unit-test for the createNewStream
method:
public ResponseEntity<Object> createNewStream(Long channelId) {
String url = IP + VERSION + serverName + VHOSTS + vhostName + APP_NAME + appName + STREAM_FILES;
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers.setAcceptCharset(Arrays.asList(Charset.forName(UTF_8)));
RestTemplate restTemplate = new RestTemplate();
String udp = "udp://" + "localhost" + ":" + "1935";
Map<String, String> map = new HashMap<>();
map.put("name", STREAMS + appName + channelId);
map.put("serverName", serverName);
map.put("uri", udp);
HttpEntity<Map<String, String>> request = new HttpEntity<>(map, headers);
HttpStatus statusCode = null;
try {
ResponseEntity<Object> response = restTemplate.postForEntity(url, request, Object.class);
statusCode = response.getStatusCode();
map.put(MESSAGE, "successful");
return new ResponseEntity<>(map, statusCode);
} catch (HttpStatusCodeException e) {
map.put(MESSAGE, e.getMessage());
return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
}
}
Upvotes: 0
Views: 4807
Reputation: 10082
RestTemplate
is a class, not an interface, and it implements the actual HTTP transport. Both are standing in the way of writing a testable method. On top of that the fact that you are constructing an instance of a class that has side effects on the OS level rather than getting it injected does not help the case. So the way to solve it is:
RestOperations
in this caseRestOperations
, e.g. an instance of RestTemplate
for production, via a constructor argument (preferred), method argument or via a Supplier<RestOperations>
defined as a field on the classMockito.mock(RestOperations.class)
because RestOperations
just like all other Spring interfaces defines way too many method for writing a test implementation manuallySo in EncoderService
you can have:
private final RestOperations restClient;
public EncoderService(RestOperations restClient) {
this.restClient = restClient;
}
public ResponseEntity<Object> createNewStream(Long channelId) {
...
ResponseEntity<Object> response = restClient.postForEntity(...
...
}
And then in EncoderServiceTest
:
ResponseEntity<Object> expectedReturnValue = ...
RestOperations testClient = mock(RestOperations.class);
doReturn(expectedReturnValue).when(testClient).postForEntity(any(), any(), anyClass());
EncoderService service = new EncoderService(testClient);
// use the service
For the other two cases the test setup is exactly the same, just you would pass the instance into the method call instead of constructor or overwrite the supplier on the EncoderService
instance to return the testClient
.
I have answered a very similar question about ProcessBuilder
which also has side effects on the OS level and was constructed directly in the method under test here Error trying to mock constructor for ProcessBuilder using PowerMockito You can apply exactly the same tactics.
Upvotes: 2