Reputation: 31
I'm unit testing a block of code that instantiates a new URL object and calls .openConnection, gets and inputStream them maps using a jackson ObjectMapper.
try {
url = new URL(uriString);
URLConnection urlConnection = url.openConnection();
urlConnection.setDoInput(true);
InputStream inputStream = urlConnection.getInputStream();
String contentString = IOUtils.toString(inputStream);
Object = objectMapper.readValue(contentString, Object.class);
} catch (IOException e) {
throw new InternalErrorRequestException(ERROR_MESSAGE, e);
}
Mocking the objectMapper.readValue is easy enough. But how can I handle the the new URI object and the resulting uriConnection without an internet connection? Assuming that I cannot modify the source. And I cannot add any new dependency.
Is there a way to capture the new URL
object and pass in a mock without using PowerMock's whenNew()
?
Upvotes: 2
Views: 892
Reputation: 1829
I will assume what you have provided is the full method. Let's consider this as the start point.
public Object sendRequest(String uriString) {
try {
URL url = new URL(uriString);
URLConnection urlConnection = url.openConnection();
urlConnection.setDoInput(true);
InputStream inputStream = urlConnection.getInputStream();
String contentString = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
return objectMapper.readValue(contentString, Object.class);
} catch (IOException e) {
throw new RuntimeException("error", e);
}
}
As we can see, the method itself creates and manages the URL, URLConnection objects etc. In order to mock that part, we can separate the sending request and retrieving InputStream part to a separate method.
Consider this.
public Object sendRequest(String uriString) {
try {
InputStream inputStream = sendRequestAndGetResponse(uriString);
String contentString = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
return objectMapper.readValue(contentString, Object.class);
} catch (IOException e) {
throw new RuntimeException("error", e);
}
}
protected InputStream sendRequestAndGetResponse(String uriString) throws IOException {
URL url = new URL(uriString);
URLConnection urlConnection = url.openConnection();
urlConnection.setDoInput(true);
return urlConnection.getInputStream();
}
I made the sendRequestAndGetResponse
method protected (we can do package-private as well) so that we can mock it.
In order to mock that method call, we can use spies. Spies are mock objects that we can set predefined behaviors for some method calls and leaving the rest to the actual implementation of that object.
So, our test method will look like this.
@Test
void sendRequest() throws IOException {
// given:
final String content = "{}";
final String uriString = "localhost";
final DemoPurposeClass demo = new DemoPurposeClass(new ObjectMapper());
final DemoPurposeClass demoSpy = Mockito.spy(demo);
doReturn(IOUtils.toInputStream(content, StandardCharsets.UTF_8))
.when(demoSpy).sendRequestAndGetResponse(uriString);
// when:
Object result = demoSpy.sendRequest(uriString);
// then:
assertNotNull(result);
}
In the test above, I spied on the class that we want to test, and put a behavior for the sendRequestAndGetResponse
method.
Please note that, I used payload like {}
, exceptions like RuntimeException
, etc. for demo purposes. Try to apply the provided approach to your testing.
Upvotes: 1