Spring boot mock method response - java

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

Related

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

Void Method in Service is not Mocking in Integration Testing

In my spring boot project, one of my Service depends on external service like Amazon. I am writing the integration testing of the Controller classes. So, I want to mock the method in the AmazonService class(as it depends on third party API). The method is void with a single Long argument and can throw a custom application-specific exceptions.
The method is as follows:-
class AmazonService{
public void deleteMultipleObjects(Long enterpriseId) {
String key = formApplicationLogokey(enterpriseId,null);
List<S3ObjectSummary> objects = getAllObjectSummaryByFolder(key);
List<DeleteObjectsRequest.KeyVersion> keys = new ArrayList<>();
objects.stream().forEach(object->keys.add(new DeleteObjectsRequest.KeyVersion(object.getKey())));
try{
DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(this.bucket).withKeys(keys);
this.s3client.deleteObjects(deleteObjectsRequest);
log.debug("All the Application logos deleted from AWS for the Enterprise id: {}",enterpriseId);
}
catch(AmazonServiceException e){
throw new AppScoreException(AppScoreErrorCode.OBJECT_NOT_DELETED_FROM_AWS);
}
}}
class Test
class Test
{
#Autowired
AmazonServiceImpl amazonService;
#Autowired
EnterpriseService enterpriseService;
#Before
public void init()
{
amazonService = Mockito.mock(AmazonServiceImpl.class);
Mockito.doNothing().when(amazonService).deleteMultipleObjects(isA(Long.class));
}
#Test
public void testDeleteEnterprise(){
setHeaders();
EnterpriseDTO enterpriseDTO = createEnterpriseEntity(null,"testDeleteEnterpriseName3",null,null,null);
String postUrl = TestUrlUtil.createURLWithPort(TestConstants.ADD_ENTERPRISE,port);
HttpEntity<EnterpriseDTO> request1 = new HttpEntity<>(enterpriseDTO,headers);
ResponseEntity<EnterpriseDTO> response1 = restTemplate.postForEntity(postUrl,request1,EnterpriseDTO.class);
assert response1 != null;
Long enterpriseId = Objects.requireNonNull(response1.getBody()).getId();
String url = TestUrlUtil.createURLWithPort(TestConstants.DELETE_ENTERPRISE,port)+File.separator+enterpriseId;
HttpEntity<EnterpriseDTO> request = new HttpEntity<>(null, headers);
ResponseEntity<Object> response = restTemplate.exchange(url,HttpMethod.DELETE,request,Object.class);
Assert.assertEquals(Constants.ENTERPRISE_DELETION_SUCCESS_MESSAGE,response.getBody());
}
}
class EnterpriseResource
class EnterpriseResource
{
#DeleteMapping("/enterprises/{enterpriseId}")
public ResponseEntity<Object> deleteEnterprise(#PathVariable Long enterpriseId) {
log.debug("REST request to delete Enterprise : {}", enterpriseId);
enterpriseService.delete(enterpriseId);
return ResponseEntity.badRequest().body(Constants.ENTERPRISE_DELETION_SUCCESS_MESSAGE);
}
}
class EnterpriseServiceImpl
class EnterpriseServiceImpl
{
#Override
public void delete(Long enterpriseId) {
log.debug("Request to delete Enterprise : {}", enterpriseId);
enterpriseRepository.deleteById(enterpriseId);
amazonService.deleteMultipleObjects(enterpriseId);
}
}
I have tried various approaches to Mock this method but it didn't work and control is going inside this method during debugging. I want to do nothing in this method during testing.
I have tried the various approaches like throw(), doNothing(), spy() etc.
Please help what is missing here?
Thanks

Mockito JUnit test for simple api call

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).

Jersey Test Framework 2.5 - test POST method

I'm trying to find some manual how to test POST methods using jersey framework, only got examples for GET method.
Here's example:
#POST
#Path("add")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response addUser(JAXBElement<User> user) {
int code = userService.addUser(user.getValue());
if (code == 500) {
return Response.status(500).build();
}
return Response.status(code).entity(user).build();
}
Could you please post some POST method test example?
Thank you in advance.
After research I did it!
Here's my solution, it works just fine.
And it's rather integration test, but we can write unit tests in similar manner.
public class RestTest extends JerseyTest{
#Override
protected Application configure() {
return new Your_Resource_Config(); //Your resource config with registered classes
}
//#Before and/or #After for db preparing etc. - if you want integration tests
#Test
public void addUserTest() {
User user = new User();
user.setEmail("user2#mail.com");
user.setName("Jane Doe");
user.getUserRoles().getRoles().add("supertester");
Entity<User> userEntity = Entity.entity(user, MediaType.APPLICATION_XML_TYPE);
target("users/add").request().post(userEntity); //Here we send POST request
Response response = target("users/find").queryParam("email", "user2#mail.com").request().get(); //Here we send GET request for retrieving results
Assert.assertEquals("user2#mail.com", response.readEntity(User.class).getEmail());
}

Play! framework 1.2.5: How to test if response is secure?

A test case for my contact formular page is to make sure it's always in a secure context respectively using SSL. Basically, all I want to know, is that I have a given request where request.secure = true;
The following response does not contain any information about this and its headers are empty:
#Test
public void shouldShowContactForm() {
Response response = GET("/contact");
// How can I ask the response, if the complete URL is in HTTPS?
}
Even if I explicitly set my own request, I cant see the right way to do this:
#Test
public void shouldShowContactFormInSSLContext() {
Request request = newRequest();
request.secure = true;
Response response = GET(request, "/contact");
// Is it now possible?
}
Is this even the right approach to test this or am I simply missing something important about the request/response?
For this question I think what I've done for my apps in the past is have a #before interceptor on all my controllers that looks like this.
#Before
static void checkSSL(){
if(Play.mode.equals(Play.Mode.PROD)){
if(!request.secure) {
Router.ActionDefinition cashTicketDefinition = Router.reverse(request.controller + "." + request.actionMethod);
cashTicketDefinition.absolute();
String url = cashTicketDefinition.url.replaceFirst( "http:", "https:");
redirect(url, true);
}
}
}

Categories

Resources