How to mock a constructor object properties using Mockito? - java

I am trying to mock a constructor 'EmailParams' in my test class.
Mocking is failing since the constructor EmailParams mocks as null.
Below is my test method
#Test
public void getContactEmailsByFilterSuccessTest() throws Exception {
String contactId = "752";
String emailAddress = "test#gmail.com";
String emailType = "EW";
MockHttpServletRequest request = new MockHttpServletRequest();
when(helper.isNumeric(any(String.class))).thenReturn(true);
List<ContactXref> sourcedContacts = getContactXrefs();
when(contactXrefServiceMock.getContactsForId(contactId)).thenReturn(sourcedContacts);
EmailParams emailParams = new EmailParams("test#gmail.com", "EW", sourcedContacts.get(0).getContact().getContactId().toString());
List<Email> emailsList = getEmailsList();
when(emailServiceMock.getEmailByFilter(emailParams)).thenReturn(emailsList);
ResponseEntity<List<Email>> response = contactControllerMock.getContactEmailsByFilter(request, contactId, emailAddress, emailType);
Assert.assertEquals("getContactEmailsByFilterSuccessTest: Expected response code to be 200", "200",
response.getStatusCode().toString());
}
This is the method I am trying to mock. Test fails when its trying to mock the constructor
#GetMapping(value = "/{contactId}/" + UrlMapping.EMAILS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Email>> getContactEmailsByFilter(HttpServletRequest request,
#PathVariable(name = RequestParams.CONTACTID) String contacId,
#RequestParam(required = false, name = RequestParams.EMAILADDRESS) String emailAddress,
#RequestParam(required = false, name = RequestParams.EMAILTYPE) String emailType)
throws Exception {
ResponseEntity response = new ResponseEntity("Only numeric contactId is allowed", HttpStatus.BAD_REQUEST);
List<Email> emailList;
List<ContactXref> sourcedContacts;
if (helper.isNumeric(contactId)) {
sourcedContacts = contXrefService.getContactsForId(contactId);
EmailParams params = new EmailParams(emailAddress, emailType, sourcedContacts.get(0).getContact().getContactId().toString());
emailList = emailService.getEmailByFilter(params);
if (emailList != null) {
response = emailList.size() == 0 ? new ResponseEntity("No emails were found for the request", HttpStatus.NOT_FOUND) : new ResponseEntity(emailList, new HttpHeaders(), HttpStatus.OK);
} else {
response = new ResponseEntity("Encountered exception in retrieving emails", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return response;
}
Here is my class which has the constructor.
public class EmailParams {
String email;
String emailType;
String ptyId;
public EmailParams() {
super();
}
public EmailParams(String pEmail, String pEmailType, String pPtyId) {
email = pEmail;
emailType = pEmailType;
ptyId = pPtyId;
}
}
How to mock it properly? thanks in advance.

If the equals method is not overridden in EmailParams class by default Mockito uses Object.equals to compare the EmailParams passed to getEmailByFilter method. In your case both object properties have same values but still they are different objects. So either override the equals method in EmailParams or
use ArgumentMatchers.argThat
when(emailServiceMock.getEmailByFilter(ArgumentMatchers.argThat(p -> p.getPEmail().equals("test#gmail.com") && condition2 && condition3 )))
.thenReturn(emailsList);

So emailService is expected to be invoked with emailParams. The emailParams is constructed using emailAddress, emailType and a contactId. If you look closely, you'll realize that sourcedContacts in your controller is the result of contXrefService.getContactsForId(contactId).
Why is this a problem? Well, look at this line in your test:
when(contactXrefServiceMock.getContactsForEcmId(contactId)).thenReturn(sourcedContacts)
You're mocking getContactsForEcmId to return the sourcedContacts. Instead, you should be mocking getContactsForId.

Related

How to write test for this put method

I have a put method, which takes an "Accept" header with value "application/json", and a requestBody class, and returns a ResponseEntity class. Can anyone tell me how to write tests for this method
This is my put api
public ResponseEntity<String> registerUserUsingPUT(#Parameter(in = ParameterIn.DEFAULT, description = "", schema=#Schema()) #Valid #RequestBody UserInput body) {
String accept = request.getHeader("Accept");
if (accept != null && accept.contains("application/json")) {
UserInfo user = userInfoService.getByEmail(body.getEmail());
if(user==null) {
user = new UserInfo();
String email = body.getEmail();
if(email.matches("^[a-z0-9._%+-]+#[a-z0-9.-]+.[a-z]{2,4}$")) user.setEmail(email);
user.setFirstName(body.getFirstName());
user.setLastName(body.getLastName());
user.setPassword(body.getUserPassword());
user.setRole(roleService.findRoleByType("Customer"));
if(body.getPhoneNo().length()==10) user.setPhoneNumber(body.getPhoneNo());
else return new ResponseEntity<String>("Incorrect Phone number",HttpStatus.BAD_REQUEST);
user.setPoints(0);
user.setStatus("Active");
user.setWalletBalance(0);
AddressDetails address = new AddressDetails();
address.setAddress(body.getAddress());
String pincode = body.getPinCode();
if(pincode.matches("^[0-9]{1,6}$")) address.setPincode(pincode);
address.setUser(user);
userInfoService.register(user);
addressDetailsDao.save(address);
return new ResponseEntity<String>("Registered",HttpStatus.OK);
}
else {
return new ResponseEntity<String>("User Already Exists",HttpStatus.BAD_REQUEST);
}
}
return new ResponseEntity<String>(HttpStatus.NOT_IMPLEMENTED);

How to test getting parameters on the Rest service using the Post method

I'm trying to test getting parameters for processing a request using the Post method
#RestController
#RequestMapping("api")
public class InnerRestController {
…
#PostMapping("createList")
public ItemListId createList(#RequestParam String strListId,
#RequestParam String strDate) {
…
return null;
}
}
test method
variant 1
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class InnerRestControllerTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
void innerCreatePublishList() {
String url = "http://localhost:" + this.port;
String uri = "/api/createList";
String listStr = "kl";
String strDate = "10:21";
URI uriToEndpoint = UriComponentsBuilder
.fromHttpUrl(url)
.path(uri)
.queryParam("strListId", listStr)
.queryParam("strDate ", strDate)
.build()
.encode()
.toUri();
ResponseEntity< ItemListId > listIdResponseEntity =
restTemplate.postForEntity(uri, uriToEndpoint, ItemListId.class);
}
}
variant 2
#Test
void createList() {
String uri = "/api/createList";
String listStr = "kl";
String strDate = "10:21";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(uri)
.queryParam("strListId", listStr)
.queryParam("strDate ", strDate);
Map<String, String> map = new HashMap<>();
map.put("strListId", listStr);//request parameters
map.put("strDate", strDate);
ResponseEntity< ItemListId > listIdResponseEntity =
restTemplate.postForEntity(uri, map, ItemListId.class);
}
Update_1
In my project exceptions is handled thus:
dto
public final class ErrorResponseDto {
private String errorMsg;
private int status;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
LocalDateTime timestamp;
...
handler
#RestControllerAdvice
public class ExceptionAdviceHandler {
#ExceptionHandler(value = PublishListException.class)
public ResponseEntity<ErrorResponseDto> handleGenericPublishListDublicateException(PublishListException e) {
ErrorResponseDto error = new ErrorResponseDto(e.getMessage());
error.setTimestamp(LocalDateTime.now());
error.setStatus((HttpStatus.CONFLICT.value()));
return new ResponseEntity<>(error, HttpStatus.CONFLICT);
}
}
In methods, where necessary, I throw a specific exception...
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved
[org.springframework.web.bind.MissingServletRequestParameterException:
Required String parameter 'strListId' is not present]
Who knows what the error is. Please explain what you need to add here and why ?
Let's take a look on declarations of postEntity:
postForEntity(URI url, Object request, Class<T> responseType)
...
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
As you can see, first argument is either URI or String with uriVariables, but second argument is always request entity.
In you first variant you put uri String as URI and then pass uriToEndpoint as request entity, pretending that it is request object. Correct solution will be:
ResponseEntity<ItemListId> listIdResponseEntity =
restTemplate.postForEntity(uriToEndpoint, null, ItemListId.class);
Addressing your comments.
If server responded with HTTP 409, RestTemplate will throw exception with content of your ErrorResponseDto. You can catch RestClientResponseException and deserialize server response stored in exception. Something like this:
try {
ResponseEntity<ItemListId> listIdResponseEntity =
restTemplate.postForEntity(uriToEndpoint, null,
ItemListId.class);
...
} catch(RestClientResponseException e) {
byte[] errorResponseDtoByteArray = e.getResponseBodyAsByteArray();
// Deserialize byte[] array using Jackson
}

Swagger example post body - how to show JSON Body- Swagger-annotations

Requirement: I have a POST method which takes the input JSON as a String and passes it to another microservice. I don't want to create an Object (Bean) of this input JSON.
method:
#ApiOperation(notes = "example" value = "/example", consumes = ".." , method= "..")
#RequestMapping(name = "xxx" value ="/hello" ..)
#ApiResponses(..)
public #ResponseBody String getXXX (#Apiparam(name="JSONrequest", required = true) #RequestBody String JSONrequest){
}
Problem:
The generated Swagger doesn't show the input as a JSON model where all the JSON attributes are displayed.
Expectation:
I want to display my Swagger Something like this :
Definately I am missing the key thing. Any thoughts?
If changing from String to a concrete object is not okay (although that's what I would recommend you to do since it's cleaner), you can try using #ApiImplicitParams (check out their documentation)
#ApiOperation(notes = "example" value = "/example", consumes = ".." , method= "..")
#ApiImplicitParams({
#ApiImplicitParam(name = "Object", value = "Object to be created", required = true, dataType = "your.package.BodyClass", paramType = "body")
})
#RequestMapping(name = "xxx" value ="/hello" ..)
#ApiResponses(..)
public #ResponseBody String getXXX (#Apiparam(name="JSONrequest", required = true) #RequestBody String JSONrequest){
}
(not sure if you still need the #Apiparam(name="JSONrequest", required = true) bit from the method parameter)
It's an old question but since I haven't found a solution online here how I to customized the example value in the swagger documentation produce automatically by the java annotations.
I use swagger 2.0 and springfox.version 2.10.5.
The Idea is documenting the class of the request parameter that has the #RequestBody annotation. for example my method is
#ApiOperation(
value = "Start ListBuilder extraction",
response = ExtractionLogEntity.class,
produces = "application/json"
)
#PostMapping("/extraction/start")
public ExtractionLogEntity startTask(
#RequestBody(required = true) ExtractionRequest request,
In order to expose request json object example I added a #ApiModelProperty(example = "...") annotation to the properties of ExtractionRequest .
#ApiModelProperty(example = "[{ 'field':'value'}]")
#NotNull
private List<ListBuilderFieldEntity> fields;
#ApiModelProperty(example = "1000")
private String ied;
#ApiModelProperty(example = "US")
private String codebase;
And that's the result
I had the similar issue. My Service Class takes #RequestBody argument in String.
So, what I did :
Created a POJO and used #RequestBody annotation with it instead of inputString.
#RequestMapping(value = "/api/entity/{entityId}/user/query", method = {RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
ResponseEntity<String> queryUser(#PathVariable("entityId") String entityId,
#RequestBody QueryUserJsonSchemaPOJO queryUserJsonSchemaPOJO, String inputString,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return userService.queryUserService(inputString, entityId, request);
}
Created an AOP with #Around annotation which update the inputString argument.
#Around(value = "execution(* com.athmin.rest.UserController.*(..)) || execution(* com.athmin.rest.CityController.*(..)), and args(..) " +
" && #annotation(com.athmin.annotations.JSONSchemaFileName) ")
public Object validateRequestBodyAgainstJsonSchema(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object[] modifiedArgs = proceedingJoinPoint.getArgs();
for (Object o : proceedingJoinPoint.getArgs()) {
if (o instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) o;
requestBody = httpServletRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
});
for (int i = 0; i < modifiedArgs.length; i++) {
if (modifiedArgs[i] == null) { // Only inputString is null in my case
modifiedArgs[i] = requestBody;
}
}
proceedingJoinPoint.proceed(modifiedArgs);
}

405 Method Not Allowed with Spring

I have the following test for an HTTP endpoint:
public static final String DATA_PARAMETER = "data";
public static final String ID_PARAMETER = "id";
public static final String VIDEO_SVC_PATH = "/video";
public static final String VIDEO_DATA_PATH = VIDEO_SVC_PATH + "/{id}/data";
#Multipart
#POST(VIDEO_DATA_PATH)
public VideoStatus setVideoData(#Path(ID_PARAMETER) long id, #Part(DATA_PARAMETER) TypedFile videoData);
#Test
public void testAddVideoData() throws Exception {
Video received = videoSvc.addVideo(video);
VideoStatus status = videoSvc.setVideoData(received.getId(),
new TypedFile(received.getContentType(), testVideoData));
assertEquals(VideoState.READY, status.getState());
Response response = videoSvc.getData(received.getId());
assertEquals(200, response.getStatus());
InputStream videoData = response.getBody().in();
byte[] originalFile = IOUtils.toByteArray(new FileInputStream(testVideoData));
byte[] retrievedFile = IOUtils.toByteArray(videoData);
assertTrue(Arrays.equals(originalFile, retrievedFile));
}
I'm trying to implement the requirements defined by this test with the following endpoint defined in Swing:
#RequestMapping(method = RequestMethod.POST, value = "/video/{id}/data")
public void postVideoData(#PathVariable("id") long videoId,
#RequestParam("data") MultipartFile videoData) throws IOException {
if (videoId <= 0 || videoId > videos.size()) {
throw new ResourceNotFoundException("Invalid id: " + videoId);
}
Video video = videos.get((int)videoId - 1);
InputStream in = videoData.getInputStream();
manager.saveVideoData(video, in);
}
The problem is that I get a "405 Method Not Allowed" error. What am I doing wrong so that my POST method is not being recognized?
The problem is that the client interface expects a VideoStatus object returned from the server. I declared the method on the server side to return void.
I don't know if you already fix your problem, but I got the same error, because I am new with Retrofit too :).
The solution for me, was to put an Annotation to specify the response content type, in my case
#ResponseBody
Another change that I must did, was to change void for a custom status.
Hope this helps or at least gives you a light.
Rgds.
I had the same issue. RetroFit request calls must have either a return type or Callback as last argument.
So in the RetroFitted API:
#POST("/path")
public Void saveWhatever(#Body Whatever whatever);
Than in the controller it must be :
#RequestMapping(value = "/path", method = RequestMethod.POST)
public #ResponseBody Void saveWhatever(#RequestBody Whatever whatever) {
repository.save(whatever);
return null;
}

Getting rid of default values in spring rest

I'm using Spring rest with below code base:
When I invoke /info by passing string value in request body, I'm expecting the below response if this value is not present in my backend database.
{"output":-10}
but instead it returns me below response:
{"id": 0, "output":-10}
Can any one tell me how to get rid of this id default value? If there is a boolean variable in JSON mapper, then that would also get returned as
{"id": 0, "booleanVar": false, "output":-10}
Can any one tell me how to get rid of this default value?
Controller.java
#RequestMapping(value = "heartbeat", method = RequestMethod.GET, consumes="application/json")
public ResponseEntity<String> getHeartBeat() throws Exception {
String curr_time = myService.getCurrentTime();
return MyServiceUtil.getResponse(curr_time, HttpStatus.OK);
}
#RequestMapping(value = "info", method = RequestMethod.POST, consumes="application/json")
public ResponseEntity<String> getData(#RequestBody String body) throws Exception {
....
myInfo = myService.getMyInfo(myServiceJson);
return MyServiceUtil.getResponse(myInfo, responseHeader, HttpStatus.OK);
}
MyService.java
#Override
public String getCurrentTime() throws Exception {
String currentDateTime = null;
MyServiceJson json = new MyServiceJson();
ObjectMapper mapper = new ObjectMapper().configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
try {
Date currDate = new Date(System.currentTimeMillis());
currentDateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(currDate);
json.setCurrentDateTime(currentDateTime);
ObjectWriter writer = mapper.writerWithView(Views.HeartBeatAPI.class);
return writer.writeValueAsString(json);
} catch (Exception e) {
throw new Exception("Excpetion in getCurrentTime: ", HttpStatus.BAD_REQUEST);
}
}
#Override
public String getMyInfo(MyServiceJson myServiceJson) throws Exception {
MyServiceJson json = new MyServiceJson();
json.setFirstName("hhh");
json.setLastName("abc");
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(json);
}
Views.java
public class Views {
public static class HeartBeatAPI { }
}
MyServiceJson.java
#JsonSerialize(include = Inclusion.NON_NULL)
public class MyServiceJson {
private int id;
private String firstName;
private String lastName;
#JsonView(Views.HeartBeatAPI.class)
private String currentDateTime;
// Getter/Setter for the above variables here
.....
}
Use Integer class instead of int primitive type. Primitive types always hold default values, where class type defaults to null.

Categories