ServiceResponse mocked which gives a null value and not expected this null - spring-boot

I'm writing j-unit Test-cases for my services and in which i couldn't mock service Response properly, Which is giving me a null. can somebody help me in this issue.
public ResponseEntity<Void> lockGet(
#ApiParam(value = "Unique identifier for this request.", required = true) #RequestHeader(value = "service-id", required = true) String serviceId,
#ApiParam(value = "Logged in userid.", required = true) #RequestHeader(value = "user-id", required = true) String userId,
#ApiParam(value = "Unique messageid.", required = true) #RequestHeader(value = "message-id", required = true) String messageId,
#RequestHeader(value = "access-token", required = true) String accessToken,
#ApiParam(value = "Unique id of the doamin of the entity", required = true) #RequestParam(value = "lockDomainId", required = true) Long lockDomainId,
#ApiParam(value = "Unique id of the entity to be fetched", required = true) #RequestParam(value = "lockEntityId", required = true) Long lockEntityId,
HttpServletRequest request, HttpServletResponse response) {
ResponseEntity<Void> result = null;
if (request.getAttribute("user-id") != null)
userId = (String) request.getAttribute("user-id");
String logContext = "||" + lockDomainId + "|" + lockEntityId + "||";
ThreadContext.put("context", logContext);
long t1 = System.currentTimeMillis();
LOG.info("Method Entry: lockGet" + logContext);
ServiceRequest serviceRequest = AppUtils.mapGetRequestHeaderToServiceRequest(serviceId, userId, lockDomainId,
lockEntityId);
try {
ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
// set all the response headers got from serviceResponse
HeaderUtils.setResponseHeaders(serviceResponse.getResponseHeaders(), response);
result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
} catch (Exception ex) {
LOG.error("Error in lockGet", ex);
result = new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
ThreadContext.put("responseTime", String.valueOf(System.currentTimeMillis() - t1));
LOG.info("Method Exit: lockGet");
return result;
}
#Test
public void testLockGetForError() {
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
LockService service = Mockito.mock(LockService.class);
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
I tried in different scenario's which couldn't fix this issue. Can someone help me out. Thanks in advance.

From the code that you have put , the issue that i see is that you are actually mocking the LockService object but when calling the lockApiController.lockGet method the code is not actually working with the mocked LockService since lockApiController has an LockService object of it's own.
One way to solve this issue is to inject the mocked LockService
object into the lockApiController object using #Spy. This way
when the getLock() is called it will be actually called on the
mocked object and will return the mock response provided.
So in your test :
#Test
public void testLockGetForError() {
LockService service = Mockito.mock(LockService.class);
LockApiController lockApiController = Mockito.spy(new LockApiController(service));
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
So you can try passing the mocked LockService object to the spy object.
Another way is to try using the #InjectMocks to inject the mocked
object into the LockApiController.
#InjectMocks marks a field on which injection should be performed. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection – in this order. If any of the given injection strategy fail, then Mockito won’t report failure.
For example:
#Mock
Map<String, String> wordMap;
#InjectMocks
MyDictionary dic = new MyDictionary();
#Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", dic.getMeaning("aWord"));
}
For the class:
public class MyDictionary {
Map<String, String> wordMap;
public MyDictionary() {
wordMap = new HashMap<String, String>();
}
public void add(final String word, final String meaning) {
wordMap.put(word, meaning);
}
public String getMeaning(final String word) {
return wordMap.get(word);
}
}
For both of these to work , you must be having a constructor or appropriate setters to set the mock object to the LockApiController class.
Reference : https://howtodoinjava.com/mockito/mockito-annotations/

Related

unable to get the response entity from an api with mockmvc

I have an issue when I try to run my controller's unit test class. I get always a empty body in the response and I don't manage to find why.
I put here the code. Maybe someone with an external vision will be able to see the reason.
the controller:
#ResponseBody
#PostMapping(path = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Object> uploadFile(#RequestParam("file") MultipartFile multipartFileData, #RequestParam(name="jobId", required = false) String jobId) {
JobStatus result;
try {
result = this.fileService.uploadFileChunk(multipartFileData, 1, 1, jobId);
}catch (ExecutionException|InterruptedException|IOException ex){
Thread.currentThread().interrupt();
return new ResponseEntity<>(ex,HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(result,HttpStatus.OK);
}
the unit test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes= FileUploadServiceRestController.class)
public class FileUploadServiceControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#MockBean
private FileUploadServiceImpl fileService;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void testUploadFile()
throws Exception {
MockMultipartFile file
= new MockMultipartFile(
"file",
"hello.txt",
MediaType.TEXT_PLAIN_VALUE,
"Hello, World!".getBytes()
);
JobStatus job = new JobStatus("uuid", ConstantUtil.JOB_STARTED);
when(fileService.uploadFileChunk(Mockito.any(MultipartFile.class),Mockito.eq(1),Mockito.eq(1),Mockito.isNull())).thenReturn(job);
mockMvc.perform(MockMvcRequestBuilders.multipart("/file/upload").file(file))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
}
and the object which will be transfered:
public class JobStatus implements Serializable {
private static final long serialVersionUID = -4405865740177389860L;
private String jobId;
private String status;
public JobStatus() {
}
public JobStatus(String jobId, String status) {
this.jobId = jobId;
this.status = status;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
For information, this controller works well when I call it from the client. I can see that the mock is well returned when I put a breakpoint at the end of the controller, but the response body stay empty.
I add here the result of print if it could help:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /file/upload
Parameters = {}
Headers = [Content-Type:"multipart/form-data"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.iso.fileservice.controller.FileUploadServiceRestController
Method = org.iso.fileservice.controller.FileUploadServiceRestController#uploadFile(MultipartFile, String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
thanks, Mathieu
Just ran into this today.
I found the answer on Why MockMvc request retrieve empty responseBody while test succeed?
I just added My restController with #Autowired instead of #InjectMocks, after that I started to retrieve the Response Entity instead of a 500 status response

Test multipart PUT request with json data using mockMvc

I am trying to unit test a put request which takes a file and some json data as request body. following is the method i am trying to test:
#RequestMapping(
value = "/{id}",
method = RequestMethod.PUT,
produces = { "application/json" }
)
public ResponseEntity<UpdateT1Output> update(#PathVariable String id, #ModelAttribute #Valid UpdateT1Input t1) {
// implementation here
}
UpdateT1Input.java
public class UpdateT1Input {
private char[] ca;
private byte[] file;
public void setFile(MultipartFile mpfile) {
try {
file = mpfile.getBytes();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private List<Double> flpa;
private List<Double> fpa;
#NotNull(message = "id Should not be null")
private Long id;
private String str;
private Long versiono;
}
test setup
#Test
public void UpdateT1_T1Exists_ReturnStatusOk() throws Exception {
// create entity obj with default values
T1Entity entity = createUpdateEntity();
entity.setVersiono(0L);
UpdateT1Input t1Input = new UpdateT1Input();
t1Input.setId(entity.getId());
t1Input.setFlpa(entity.getFlpa());
t1Input.setStr(entity.getStr());
ObjectWriter ow = new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.writer()
.withDefaultPrettyPrinter();
String json = ow.writeValueAsString(t1Input);
MockMultipartHttpServletRequestBuilder builder =
MockMvcRequestBuilders.multipart("/t1/" + entity.getId());
builder.with(request -> {
request.setMethod("PUT");
return request;
});
mvc.perform(builder
.file("file", "ABC".getBytes("UTF-8"))
.content(json)
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(status().isOk());
}
but in controller only id and file fields are set in input dto all other fields are null. i am using #ModelAttribute to avoid dividing request into file and data parts. so is there a way that to get all the fields in single object?

MockMvc PostRequest Exception

I have following post mapping.
#PostMapping(value = BULK_UPDATE)
#ApiOperation(value = "Bulk Update of Markets by pairs of Market Key and Tier Quantity Id", tags = "Bulk", code = 200)
#ApiImplicitParams({
#ApiImplicitParam(name = "MarketTierQuantityId", value = "List of Market Key and Tier Quantity Id pairs",
paramType = "body", allowMultiple = true, dataType = "MarketTierQuantityId", required = true) })
#ApiResponses({
#ApiResponse(code = 200, message = "Bulk update successful", response = MarketStatus.class, responseContainer = "List") })
#ResponseStatus(org.springframework.http.HttpStatus.OK)
public ResponseEntity<StreamingResponseBody> bulkUpdate(
#RequestParam(name = IGNORE_SYNC_PAUSE_FAILURE, required = false, defaultValue = "false")
#ApiParam(name = IGNORE_SYNC_PAUSE_FAILURE, value = "Ignore failure of the jobs pause command") boolean ignoreJobsPauseFailure,
#RequestBody #ApiParam(name = "MarketTierQuantityId", value = "List of Market Key and Tier Quantity Id pairs", required = true) List<MarketTierQuantityId> marketTierQuantities,
#RequestParam(name = MOVE_TO_PREAUTH_FLAG, required = false, defaultValue = "true")
#ApiParam(name = MOVE_TO_PREAUTH_FLAG, value = "Move new units to Preauth for the markets with active waitlists") boolean moveToPreauth) throws BusinessException {
String requestId = getRequestId();
boolean jobsPaused = pauseJobs(ignoreJobsPauseFailure);
return LoggingStopWatch.wrap(() -> {
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON)
.body(outputStream -> process(new SyncBulkProcessorHelper(outputStream),
marketTierQuantities, jobsPaused, requestId, moveToPreauth, LoggingStopWatch.create(LOGGER, "Bulk Update")));
});
}
and i have written the following test.
#RunWith(SpringRunner.class)
#WebMvcTest(BulkUpdateController.class)
#ContextConfiguration(classes = { BulkUpdateController.class, SharedExecutor.class })
#ActiveProfiles("dev")
public class BulkUpdateControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private BulkStatusService bulkStatusService;
#MockBean
private BulkMarketService bulkMarketService;
#MockBean
private HttpService httpService;
#MockBean
private RestClient restClient;
#MockBean
private BulkProcessorHelper helper;
#Test
public void test() throws Exception {
String request = TestHelper.getSerializedRequest(getBulkUpdateRequest(), MarketTierQuantityId.class);
mockMvc.perform(post("/bulkupdate").accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON)
.content(request)).andExpect(status().is4xxClientError());
}
public MarketTierQuantityId getBulkUpdateRequest() {
MarketTierQuantityId market = new MarketTierQuantityId();
market.setMarketKey("00601|PR|COBROKE|POSTALCODE|FULL");
market.setTierQuantityId("10");
return market;
}
Getting the following error, have tried every possible way to resolve it but doesnt help.
Request failed. Error response:
{\"responseStatus\":{\"errorCode\":\"BadRequest\",\"message\":\"JSON
parse error: Cannot deserialize instance of java.util.ArrayList out
of START_OBJECT token\",\"stackTrace\":\"BusinessException(JSON parse
error:
P.S -> new to JUnits and mocks

Java: GroupSequenceProvider for Validation, object is null in getValidationGroups method

This is what I am trying to achieve:
I have an update request object and user is allowed to do Partial Updates. But I want to validate the field only if it is in the request body. Otherwise, it is OK to be null. To achieve this, I am using GroupSequenceProvider to let the Validator know what groups to validate. What am I doing wrong here? If there is a blunder, how do I fix it?
Documentation: https://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-groups.html#example-implementing-using-default-group-sequence-provider
#GroupSequenceProvider(UpdateUserRegistrationGroupSequenceProvider.class)
public class UpdateUserRegistrationRequestV1 {
#NotBlank(groups = {EmailExistsInRequest.class})
#Email(groups = {EmailExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {EmailExistsInRequest.class})
private String email;
#NotNull(groups = {PasswordExistsInRequest.class})
#Size(min = 8, max = 255, groups = {PasswordExistsInRequest.class})
private String password;
#NotNull(groups = {FirstNameExistsInRequest.class})
#Size(max = 255, groups = {FirstNameExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {FirstNameExistsInRequest.class})
private String firstName;
// THERE ARE GETTERS AND SETTERS BELOW
}
Group Sequence Provider Code:
public class UpdateUserRegistrationGroupSequenceProvider implements DefaultGroupSequenceProvider<UpdateUserRegistrationRequestV1> {
public interface EmailExistsInRequest {}
public interface PasswordExistsInRequest {}
public interface FirstNameExistsInRequest {}
#Override
public List<Class<?>> getValidationGroups(UpdateUserRegistrationRequestV1 updateUserRegistrationRequestV1) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add(Default.class);
defaultGroupSequence.add(UpdateUserRegistrationRequestV1.class);
if(StringUtils.hasText(updateUserRegistrationRequestV1.getEmail())) {
defaultGroupSequence.add(EmailExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getPassword())) {
defaultGroupSequence.add(PasswordExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getFirstName())) {
defaultGroupSequence.add(FirstNameExistsInRequest.class);
}
return defaultGroupSequence;
}
}
I am using Spring MVC, so this is how my controller method looks,
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable("userId") Long userId,
#RequestBody #Valid UpdateUserRegistrationRequestV1 request) {
logger.info("Received update request = " + request + " for userId = " + userId);
registrationService.updateUser(userId, conversionService.convert(request, User.class));
}
Now the problem is, the parameter "updateUserRegistrationRequestV1" in the UpdateUserRegistrationGroupSequenceProvider.getValidationGroups method is null. This is the request object that I am sending in the request body and I am sending email field with it.
What am I doing wrong?
I too went through the same issue ,and hopefully solved it
You just have to check the object is null and put all your conditions inside it.
public List<Class<?>> getValidationGroups(Employee object) {
List<Class<?>> sequence = new ArrayList<>();
//first check if the object is null
if(object != null ){
if (!object.isDraft()) {
sequence.add(Second.class);
}
}
// Apply all validation rules from default group
sequence.add(Employee.class);
return sequence;
}

Spring Rest Issue

I am getting an error while i am trying to test my "testCreateUser" method using Spring RestApi, the uploadNewUser.xml contains the login information about the user and the role.
#Test
public void testCreateUser() throws Exception {
Reader reader = getFileReader("src/test/resources/uploadNewUser.xml");
String input_xml = IOUtils.toString(reader);
byte[] content = input_xml.getBytes();
request.addHeader("Accept", "application/xml");
request.addHeader("Content-Type", "application/xml");
request.setContent(content);
request.setContentType("text/xml");
request.setMethod(RequestMethod.POST.name());
request.setRequestURI("/restapi/users/");
final ModelAndView mav = handle(request, response);
Map<String, Object> map = mav.getModel();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
UserCollection collection = (UserCollection) entry.getValue();
org.springframework.validation.BindingResult.error = com.xxx.dashboard.restapi.GlobalResponse#42a4fd6d
error stack:
java.lang.ClassCastException: com.xxx.dashboard.restapi.GlobalResponse cannot be cast to com.xxx.dashboard.restapi.UserCollection
and i am getting an issue with cannot cast GlobalRespose to UserCollection. can anyone tell me where exactly i am doing is wrong? any help or pointers are most welcome thanks in advance
#Controller("userrestapi")
#RequestMapping(value = { "/restapi/users/", "/restapi/users" })
public class UserRestApi extends AbstractBaseApi {
...
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.CREATED)
public ModelAndView createNewUser(#RequestBody UserCollection userCollection,
#RequestHeader(value = "accept", required = false) String accept,
#RequestHeader(value = "version", required = false) String version) {
try {
OOUser ooUser = userCollection.getUsers().get(0);
Mapper mapper = (Mapper) userVersions.get(Constants.USER_DETAIL_VERSION_MAPPER_KEY);
int userId = usersRestApiService.validateAndCreateNewUser(ooUser, mapper);
List<FilterField> filterFieldList = new ArrayList<FilterField>();
filterFieldList.add(new FilterField("userId", String.valueOf(userId)));
return getUserDetailsForFilter(filterFieldList, accept, version, mapper);
} catch (Exception ex) {
logger.warn("Api exception", ex);
return getModelAndView(accept, "error", getGlobalResponse(ex));
}
the abstractbaseapi contains following
public class AbstractBaseApi {
public static final String XML_VIEW = "apiXmlView";
public static final String JSON_VIEW = "apiJsonView";
public static final String JSON_ACCEPT_HEADER = "application/json";
public static final String JSON_CONTENT_HEADER = "Content-type: application/json";
public static final String XML_CONTENT_HEADER = "Content-type: text/html;charset=utf-8";
public static final int MAX_COUNT = 100;
public static final String XML_REQUEST_ERROR_FORMAT = "<?xml version='1.0' encoding='UTF-8'?><GlobalResponse xmlns='http://www.operative.com/api' xmlns:v2='http://www.operative.com/api/v2' xmlns:v1='http://www.operative.com/api/v1'> <error errorCode='%1$s' text='%2$s'/> </GlobalResponse>";
public static final String JSON_REQUEST_ERROR_FORMAT = "{error:{errorCode:'%1$s',text:'%2$s'}}";
protected final Logger logger = Logger.getLogger(this.getClass());
protected ModelAndView getModelAndView(String accept, String key, Object value) {
String view = XML_VIEW;
if (accept != null && accept.toLowerCase().contains(JSON_ACCEPT_HEADER)) {
view = JSON_VIEW;
}
if (logger.isDebugEnabled()) {
logger.debug("Accept Header:" + accept + " , generating:" + view);
}
return new ModelAndView(view, BindingResult.MODEL_KEY_PREFIX + key, value);
}
Your model contains more than you think.
You are going through your model and looking for your user collection. However, the first encountered object in your map seems to be the GlobalResponse map.
You should probably just get it by name from the model, i.e.
UserCollection collection = (UserCollection) mav.getModel().get("userCollection");
rather than iterating..

Resources