Mockito JUnit test for simple api call - java

I consider myself a novice at unit-testing, completely new to Mockito and junit. I have to write unit-tests for some simple api-calls. But my test seems somewhat pointless to me, I can't tell where I am going wrong. I have added a method to an existing web-service, ManagerWS.java , See Below.
ManagerWS.java Method:
public String healthCheck(String userId) {
String healthCheckUrlEndpoint = this.baseUrl()+"/health";
logger.debug("Calling health check: {}", healthCheckUrlEndpoint);
HttpHeaders healthCheckHeaders = new HttpHeaders();
healthCheckHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
healthCheckHeaders.add(USER_KEY, userId);
healthCheckHeaders.add(TOKEN_NAME, TOKEN_VALUE);
healthCheckHeaders.add(Constants.ACCEPT_LANGUAGE_HEADER, LocaleContextHolder.getLocale().toString());
healthCheckHeaders.add(CORRELATION_HEADER, myService.get(AppLoggingMDCService.LOG_KEY_REQUEST_ID));
HttpEntity<Object> request = new HttpEntity<Object>(healthCheckHeaders);
ResponseEntity<String> response;
try {
response = makeRequest(HttpMethod.GET, healthCheckUrlEndpoint, request, String.class);
} catch (Exception e) {
logger.error("Exception encountered during health check", e);
throw e;
}
logger.debug("RESPONSE : http status: {} - body: {}", response.getStatusCode(), response.getBody());
return response.getStatusCode().toString();
}
The logic is simple. Construct the url, create headers and add headers to the request. make the request and extract the status-code from the response. Here is my test. NOTE: the class is using #RunWith(SpringRunner.class) and I am using #Mock for dependencies and #InjectMocks for the local instance ManagerWS. ManagerWS.java is the service calss being tested.
TEST-CLASS TEST-Method:
#Test
public void testHealthCheck() throws Exception {
//Given
managerWS = new ManagerWS(templateFactory, configParamService, mdcService, env);
String url = "http://baseurl/health";
HttpHeaders headers = new HttpHeaders();
HttpEntity<Object> request = new HttpEntity<Object>(headers);
ResponseEntity<String> response = new ResponseEntity<>(HttpStatus.OK);
//when
when(managerWS.makeRequest(HttpMethod.GET, url, request, String.class)).thenReturn(response);
String actualStatus = response.getStatusCode().toString();
//then
Assert.assertEquals("200",actualStatus);
}
To me this test seems stupid (for want of a batter word). I basicall set the status to give a "200" and assert that what i set is "200". That is not really making much sense.To me it literally does nothing. I tried using spy(ManagerWS.class). But I am literally grasping at straws without the full understanding.
SonarQube still complains with "Not covered by unit tests". I cam completely stumped as to how else to write this test. I also have to do similar tests for three other calls.
I am a total novice to testing and I cannot see my mistake. Please advise.

SonarQube still complains with "Not covered by unit tests".
Your unit test doesn't test from the entry point of the method to test : healthCheck(String), so it is not covered by unit tests.
Besides, you also mock the part of the method that you want to test :
when(managerWS.makeRequest(HttpMethod.GET, url, request, String.class)).thenReturn(response);
So indeed your approach looks wrong.
In fact, writing an unit test for this code looks wrong too or at least looks like a white box test with few value.
Why ?
Your logic depends on :
response = makeRequest(HttpMethod.GET, healthCheckUrlEndpoint, request, String.class);
But you can know if it works only at runtime, with a running HTTP Server.
So the single thing that you can do is mocking everything, spying the object under test and verifying that each statement in the implementation is performed : no readable test, no robust and few/no value.
Your method that relies essentially on side effect would make more sense to be tested with as an integration test :
ManagerWS managerWS; // real ManagerWS implementation without mock
#Test
public void healthCheck() throws Exception {
//Given
String url = "http://baseurl/health";
// When
String actual managerWS.healthCheck(url);
// Then
String expected = "...";
Assertions.assertEquals(expected, actual);
}
As a side note, if you used Spring, I would advise you to look at test slicing #WebMvcTest that focuses on the web part of the component under test. It allows mainly to test the HTTP part/logic (headers, request, response).

Related

Spring boot mock method response

I need some help with unit tests. Our company has decided to start using TDD, and I'm supposed to implement that, but I've got very limited experience with unit tests in general, so I'm trying to cover some of the old code to get up to speed. That's when I got stuck with this:
public Analytics generateAnalytics(String id, String domain) {
List<Result> totalResults = new ArrayList<>();
for(String url : ANALYTIC_URLS) {
url += domain;
String scrubbedUrl = url.replace(API_KEY, "XXXXXXXXXX");
Audit audit = new Audit(id, scrubbedUrl);
try {
totalResults.add(new Result(getData(url, audit)));
} catch(Exception e) {
audit.setResponse(e.toString());
throw new Exception(e);
} finally {
auditRepository.save(audit);
}
}
return composeAnalytics(totalResults);
}
private List<Map<String, String>> getData(String request, Audit audit) throws Exception {
try (CloseableHttpClient client = HttpClients.createDefault()) {
CloseableHttpResponse response = client.execute(new HttpGet(request));
if(response.getStatusLine().getStatusCode() == 200) {
return readInputStream(response.getEntity().getContent(), audit);
} else {
throw new Exception(response.toString());
}
}
}
My issue is, when I wanna test the generateAnalytics method, the getData method goes and gets data from a live API that costs units per each request. Obviously I wanna stop this from bleeding out all our units during the testing. I've tried mocking the ClosableHttpClient like so:
#Mock
CloseableHttpClient client;
#InjectMocks
Service service;
#Test
void testTest() throws Exception {
when(client.execute(any())).thenReturn(mock(CloseableHttpResponse.class));
service.generateAnalytics("123", "no.com");
assertEquals(true, true);
}
This works when there's another service that needs to be mocked in one of my other tests, but in this case it still calls the API and drains our units. What should I do about this?
That's because your client mock is never used:
try (CloseableHttpClient client = HttpClients.createDefault()) {
Either mock HttpClients.createDefault() or , maybe better, inject the client into the service instead of creating on the fly.
Have you try to use MockMvc?
With MockMvc you can perform requests against a mocked servlet environment.
There won't be any real HTTP communication for such tests.
Official documentation: spring.io/testing-web

Do I handle NulPointerException in my method or fix my unit test?

I have this method in my Java application:
#Override
public List<Client> getClients(String username) {
ParameterizedTypeReference<List<Client>> tRef = new ParameterizedTypeReference<List<Client>>() {
};
HttpHeaders headers = createHeaders();
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<List<Client>> response = restTemplate.exchange(aPIEndpoint + Constants.GET_CLIENTS,
HttpMethod.GET, request, tRef, username);
if (response.getStatusCode().is2xxSuccessful()) {
return response.getBody();
} else {
return new ArrayList<>();
}
}
I noticed in one of my unit tests that line if (response.getStatusCode().is2xxSuccessful()) { causes a NullPointerException. I'm wondering how I should deal with this. Should I handle this exception in my getClients() method or does my test need fixed in some way?
Just fixing your unit test won’t help handling real error cases.
To me, your method would definitely benefit from a try / catch as it performs an external call which you can’t predict.
Then, you could mock your API call and simulate various scenarios to make sure you handle all cases (success or failure) with unit tests.
You get NPE cause restTemplate.exchange(..) returns Null response.
RestTemplate can be mock object and you can return mock response.
using RestTemplate as mock. https://www.baeldung.com/spring-mock-rest-template
or use TestRestTempate. https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/web/client/TestRestTemplate.html

How to mock multipart/form-data with Java and Spark?

I'm receiving a file using multipart/form-data like I'll show you right below (Using Spark, not SpringBoot):
#Override
public Object handle(Request request, Response response) throws Exception {
request.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement(""));
Part filePart = request.raw().getPart("file");
String regex = request.raw().getParameter("regex");
String fileName = filePart.getSubmittedFileName();
byte[] fileBytes = filePart.getInputStream().readAllBytes();
The thing is, I want to unit test this Controller, and in order to do so, I need a way to mock the request to have a multipart/form-data inside, or at least a way to use "when...theReturn" to mimic that part of code...
Any ideas?
Thanks in advance!
So I managed to find the answer to this question and I thought maybe I could help other people by answering it:
#Test
public void whenValidRegex_returnOkAndTotalAmount() throws Exception {
final Part file = mock(MultiPartInputStreamParser.MultiPart.class);
final Request mock = mock(Request.class); //Spark request
final Response mockResponse = mock(Response.class); //Spark response
final HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); //Javax servlet
when(mock.raw()).thenReturn(httpServletRequest);
when(file.getSubmittedFileName()).thenReturn("file.pdf");
when(mock.raw().getParameter("regex")).thenReturn(String.valueOf("SOME REGEX"));
when(mock.params("numPage")).thenReturn(String.valueOf("1"));
when(file.getInputStream()).thenReturn(IOUtils.toInputStream("ARGENTINIAN PESOS", Charset.defaultCharset())); //Here you are mocking the input stream you might get from the part file.
when(mock.raw().getPart("file")).thenReturn(file);
Now that you have the multipart/form-data mocked, you can continue your test mocking the service calls and such things.
Please ignore things that are from my specific code, like the "Page number" mocking, you don't need that.
Hope this helps for someone else.
Bye!

doThrow Exception not throwing for spring MVC unit test?

ive tried switch to a when() and get compiler errors
i have successfully used doThrow(...) in another test in the same project, so i dont know whats going on here
Unit test code:
doThrow(new Exception("the client cancelled the request, ya dingus!")).when(handler).write(any());
MvcResult result = mockMvc.perform(post("/myPath")
.content(String.valueOf(mockValidRequest))
.contentType(MediaType.APPLICATION_JSON)
.characterEncoding("utf-8"))
.andExpect(status().is5xxServerError())
.andReturn();
Code I am testing (handler method for /myPath):
#PostMapping("/myPath")
public ResponseEntity<String> handleRequest(#RequestBody MyPojo request) {
try {
handler.write(request);
} catch (Exception e) {
return new ResponseEntity<>("Exception thrown during event processing: " + e, HttpStatus.SERVICE_UNAVAILABLE);
}
return new ResponseEntity<>("Success", HttpStatus.OK)
}
The problem is the test says the actual result is a 200 success, when there should have been an exception caught, and a service unavailable 5xx response.
So I found the proper answer to my issue:
I didnt have this snippet in my original post, but the handler was being instantiated in the test like this:
#Mock
EventHandler handler;
it needed to be:
#MockBean
EventHandler handler;
My guess is since #MockBean is the spring mock, and #Mock is from Mockito, it was probably mocking an event handler that was instantiated outside of the Spring container. Thus why I didnt think it was picking up the doThrow ... it did pick it up, but spied on the wrong instance.
i found that i could get the tests to pass calling the function directly ... it just seems like MockMvc is not incorporating the doThrow logic
revised unit test:
doThrow(new Exception("some error")).when(handler).sendToSqsWriter(any());
ResponseEntity<String> response = controller.handleRequest(new Gson().fromJson(new JSONObject(mockApptRequestBody);.toString(), SeamAppointmentRequest.class));
assertTrue(response.toString().contains("some error"));
assertTrue(response.getStatusCodeValue() == 503);
all the json/gson jazz is resolving runtime parsing errors

Mockito servlet test: cannot use response - it's not committed

I've made a basic test for servlet to test it's response status code, but it doesn't work - it's always 0, although I've set the response status code inside the servlet to 200.
public class TestMyServlet extends Mockito {
#Test
public void test() throws Exception {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
when(request.getParameter("test")).thenReturn("1");
new MyServlet().doPost(request, response);
System.out.println(response.isCommited()); // false
System.out.println(response.getContentType()); // null
System.out.println(response.getStatus()); // 0
}
}
How to let this work?
You want to test this differently. You need to verify that your inputs caused the expected outputs. For non-mock results, you would assert the behavior. Since you want to verify that your outputs were set properly.
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class MyServletTests {
#Test
public void testValidRequest() throws Exception {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
when(request.getParameter("test")).thenReturn("1");
new MyServlet().doPost(request, response);
// ensure that the request was used as expected
verify(request).getParameter("test");
// ensure that the response was setup as expected based on the
// mocked inputs
verify(response).setContentType("text/html");
verify(response).setStatus(200);
}
}
If you expect something to not be touched given certain inputs, then you should consider verifying that behavior using verify(response, never()).shouldNotBeCalledButSometimesIs() (to verify when conditions control it being called/set versus not).
You're mocking HttpServletResponse. So, since it's a mock, getStatus() will only return a non-zero value until you tell the mock to return something else when getStatus() is called. It won't return the value passed to setStatus(), which, since it's a mock, doesn't do anything.
You could use a "smarter" mock HttpServletResponse, like the one provided by Spring.

Categories

Resources