How to mock BindingResult in Spring Boot test? - spring-boot

In a TestClass I added #Import(MandatairesFormValidator.class)
When I mock the controller I can go inside the controller but I can't access the method validate.validate() as suggested here: How to mock BindingResult in Spring Boot Test
The controller is:
#PatchMapping(value = "/{id}", consumes = APPLICATION_JSON_UTF8_VALUE, produces = APPLICATION_JSON_UTF8_VALUE)
ResponseEntity<Object> modifierDonnees(#PathVariable final Long id,
#Valid #RequestBody final ModificationUI modificationUI, BindingResult bindingResult) {
validator.validate(modificationUI, bindingResult);
if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toList());
throw new MandataireValidationException(errors);
}
}
The UnitTestController:
#RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
ResultActions resultActions = mockMvc.perform(patch(URL_RACINE_CTRL + "/1").contentType(APPLICATION_JSON_UTF8_VALUE)
.content(
"{\"modificationMandataire\":{\"mandataireOrigine\":{}, " +
"\"mandataireSoustraction1\":{}, " +
"\"mandataireSoustraction2\":{}}}")).andExpect(MockMvcResultMatchers.status().isBadRequest());
using: #Import(MandatairesFormValidator.class) the validator in the Controller is null ;
using: #Mock
private MandatairesFormValidator mandatairesFormValidator; it skips the 'validator.validate()' method -->
BindingResult bindingResult = mock(BindingResult.class);
when(bindingResult.hasErrors()).thenReturn(true);
The validator:
#Component
public class MandatairesFormValidator implements Validator {
public static final String international_number="(0{1,2}|\\+\\d{2,})(-|\\.|\\s*|\\d*)*";
public boolean supports(Class<?> clazz) {
return ModificationUI.class.isAssignableFrom(clazz);
}
private Boolean isValidNatel(String telephone){
return Objects.nonNull(telephone) && (telephone.matches(international_number));
}
public void validate(Object o, Errors errors) {
ModificationUI modificationUI = (ModificationUI) o;
ModificationMandataireUI modificationMandataire = modificationUI.getModificationMandataire();
MandataireSoustractionUI mandataireSoustractionUI1 = modificationAffaireMandataire.getMandataireSoustraction1();
MandataireSoustractionUI mandataireSoustractionUI2 = modificationAffaireMandataire.getMandataireSoustraction2();
Boolean isValidNatel1 = (Objects.nonNull(mandataireSoustractionUI1) && isValidNatel(mandataireSoustractionUI1.getTelephone()) || Objects.isNull(mandataireSoustractionUI1));
Boolean isValidNatel2 = (Objects.nonNull(mandataireSoustractionUI2) && isValidNatel(mandataireSoustractionUI2.getTelephone()) || Objects.isNull(mandataireSoustractionUI2));
if ( !isValidNatel1 ) {
String telephone1=mandataireSoustractionUI1.getTelephone();
String msg1= Objects.nonNull(telephone1) ? telephone1 : "";
errors.reject("telephone", "Téléphone 1 " + msg1 +" pas valide");
}
if ( !isValidNatel2 ) {
String telephone2=mandataireSoustractionUI2.getTelephone();
String msg2= Objects.nonNull(telephone2) ? telephone2 : "";
errors.reject("telephone", "Téléphone 2" + msg2 + " pas valide");
}
}
}`
I tried using

Related

spring resttemplate request object not mapping to rest controller

i have below resttempalte which invokes rest controller of another service..
#Override
public ResponseEntity<String> callRestAPI(APIReqDataMO apiReqDataMO) {
String apiURL = URIGenerator.getAPIURL(apiReqDataMO);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<?> request = new HttpEntity<>(apiReqDataMO.getRequestObject(), headers);
ResponseEntity<String> httpRes = restTemplate.postForEntity(apiURL, request, String.class);
return httpRes;
}
and in my service i have controller, which consumes above request..
#RequestMapping(value = "/targetService/createUser", method = RequestMethod.POST, consumes = "application/json")
public String fuzzerServiceAge(UserMO userMO) {
System.out.println("---------------------age is -------------------------" + userMO.getAge());
if (userMO.getAge() > 0) {
System.out.println("error age greater than 0 ");
return "invalid user age";
} else if (userMO.getAge() == 0) {
return "invalid user age";
}
return "user added successfully";
}
when i try my test.. the age which i am pushing through rest template is not getting mapped..and i am getting age as 0 always in my system.out.. what could be wrong in my code... and is there anything missing from configuration perspective..
EDIT -
public class APIReqDataMO {
private String restAPIURL;
private Object[] pathParam;
private Object[] requestParam;
private String requestType;
private String paramType;
private Object requestObject;
public String getParamType() {
return paramType;
}
public void setParamType(String paramType) {
this.paramType = paramType;
}
public String getRequestType() {
return requestType;
}
public void setRequestType(String requestType) {
this.requestType = requestType;
}
public Object getRequestObject() {
return requestObject;
}
public void setRequestObject(Object requestObject) {
this.requestObject = requestObject;
}
public String getRestAPIURL() {
return restAPIURL;
}
public void setRestAPIURL(String restAPIURL) {
this.restAPIURL = restAPIURL;
}
public Object[] getPathParam() {
return pathParam;
}
public void setPathParam(Object[] pathParam) {
this.pathParam = pathParam;
}
public Object[] getRequestParam() {
return requestParam;
}
public void setRequestParam(Object[] requestParam) {
this.requestParam = requestParam;
}
}
controller
#PostMapping("/targetService/createUser")
public String fuzzerServiceAge(UserMO userMO) {
System.out.println("--------------------- age is -------------------------" + userMO.getAge());
if (userMO.getAge() > 0) {
// return ResponseEntity.ok("Hello World!");
} else if (userMO.getAge() == 0) {
System.out.println(" it is else block");
// return ResponseEntity.badRequest().build();
}
// return ResponseEntity.ok("user added successfully!");
return "user added successfully";
}
usermo
public class UserMO {
#JsonProperty("name")
private String name;
#JsonProperty("age")
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Issue
There is an issue in API implementation. You are creating POST API and when the user will invoke this API by passing UserMO in the request body then mapping won't happen because the #RequestBody annotation is missing.
#PostMapping("/targetService/createUser")
public String fuzzerServiceAge(UserMO userMO) {
System.out.println("--------------------- age is -------------------------" + userMO.getAge());
if (userMO.getAge() > 0) {
// return ResponseEntity.ok("Hello World!");
} else if (userMO.getAge() == 0) {
System.out.println(" it is else block");
// return ResponseEntity.badRequest().build();
}
// return ResponseEntity.ok("user added successfully!");
return "user added successfully";
}
Solution
If you are using #RestController annotation on top of the controller class then add #RequestBody annotation before UserMO userMO and try again.
Like this
#PostMapping("/targetService/createUser")
public String fuzzerServiceAge(#RequestBody UserMO userMO) {
//logic
}
if you are using #Controller annotation on top of the controller class then add #ResponseBody annotation on top of method fuzzerServiceAge() and #RequestBody annotation before UserMO userMO and try again.
Like this
#PostMapping("/targetService/createUser")
#ResponseBody
public String fuzzerServiceAge(#RequestBody UserMO userMO) {
//logic
}

How spring mvc handle responsebody and view excpetion?

I have a controller as
#Controller
#RequestMapping("/test")
public class TestController {
#RequestMapping("/1")
#ResponseBody
public String test1(){
Object o = null;
o.toString();
return "I ma test one!";
}
#RequestMapping("/2")
public String test2(){
Object o = null;
o.toString();
return "test";
}
}
Is it possible to create ControllerAdvice(s) to handle the controller method as different result without moving these to message to different classes.
I mean:
1. test1 returns a String message: if there is exception, handle it with handleError1 and return a message.
2. test1 returns a view : if there is exception, handle it with handleError2 and return/redirect to a view.
#ControllerAdvice
public class AdviceController {
#ExceptionHandler({ NullPointerException.class })
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public Map handleError1(IllegalStateException ex, HttpServletRequest request) {
Map map = new HashMap();
map.put("code","1000");
map.put("message","NullPointerException of Object");
return map;
}
#ExceptionHandler(NullPointerException.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public String handleError2(MultipartException e, RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", e.getCause().getMessage());
redirectAttributes.addFlashAttribute("code", "1000");
return "redirect:/error";
}
}
if use
#ControllerAdvice(annotations=RestController.class)
#ControllerAdvice(annotations=Controller.class)
We need to create more controllers.

Spring #PreAuthorize passes null to Service [duplicate]

This question already has an answer here:
Spring - SpEL evaluates entity argument as null reference in #PreAuthorize("hasPermission")
(1 answer)
Closed 5 years ago.
I have an issue with #PreAuthorize and a sevice that checks if the athenticated user may access the searched item.
The one service callDistributionRequest that gets the item is working fine - #PreAuthorize recieves and passes the right distId. The other one updateDistributionRequestExportFileName gets also the right distId and passes it to the distributionRequestService. On the method userBelongsToRecipientOfTheDistributionRequest distId comes as a null
The Spring RestController with the two web services
#RestController
#RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class DistributionRequestRESTController {
#Autowired
private #Getter #Setter DistributionRequestService distributionRequestService;
private final Logger log = LoggerFactory.getLogger(this.getClass());
private String logResponse = " - response: ";
#Autowired
public DistributionRequestRESTController(DistributionRequestService distributionRequestService) {
this.distributionRequestService = distributionRequestService;
}
#RequestMapping(value = Consts.URLDISTRIBUTIONREQUEST + Consts.URLDISTREQID)
public DistributionRequest callDistributionRequest(#PathVariable long distId) {
String loginfo = "get distribution with id: " + distId;
//log.info(loginfo);
DistributionRequest found = distributionRequestService.findOne(distId);
log.info(loginfo + logResponse + JSONParser.toJsonString(found));
return found;
}
#RequestMapping(method = RequestMethod.POST, value = Consts.URLDISTRIBUTIONREQUEST + Consts.URLDISTREQID + Consts.URLUPDATE + Consts.URLFILENAME)
public DistributionRequest updateDistributionRequestExportFileName(
#PathVariable long distId,
#RequestBody String fileName,
#AuthenticationPrincipal UserDetails user) {
String loginfo = user.getUsername() + " try to update filename with : " + fileName;
//log.info(loginfo);
DistributionRequest updated =
distributionRequestService.updateExportFilename(distId, fileName);
log.info(loginfo + logResponse + JSONParser.toJsonString(updated));
return updated;
}
}
The Service interface:
public interface DistributionRequestService {
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest findOne(Long distId);
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest updateExportFilename(Long distId, String filename);
}
And the class that checks if the user may access the searched item
#Service(value = "distributionRequestOwnerService")
public class DistributionRequestOwnerServiceImpl implements DistributionRequestOwnerService {
#Autowired
private AccountService accountService;
#Autowired
private DistributionRequestsRepository distributionRequestsRepository;
#Override
public boolean userBelongsToRecipientOfTheDistributionRequest(Long distId) {
return userBelongsToRecipientOfTheDistributionRequest(distId, null);
}
#Override
public boolean userBelongsToRecipientOfTheDistributionRequest(Long distributionRequestId, String username) {
DistributionRequest distributionRequest = distributionRequestsRepository.findOne(distributionRequestId);
ServiceAccount currentUser;
if (username == null)
currentUser = accountService.getCurrentUser();
else
currentUser = accountService.findByUsername(username);
if (distributionRequest != null
&& distributionRequest.getRecipientId() == currentUser.getRecipientId())
return true;
throw new AercacheWSException(Consts.EXCEPTIONMISSINGELEMENTORPERMITION);
}
}
Any ideas?
thanks in advance
Found the solution duplicate to
as #teppic pointed parameter in interfaces should be annotated.
public interface DistributionRequestService {
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest findOne(#Param("distId") Long distId);
#PreAuthorize(value = "hasAnyAuthority('USER', 'ADMIN') and #distributionRequestOwnerService.userBelongsToRecipientOfTheDistributionRequest(#distId)")
DistributionRequest updateExportFilename(#Param("distId") Long distId, String filename);
}

#PathVariable Validation in Spring 4

How can i validate my path variable in spring. I want to validate id field, since its only single field i do not want to move to a Pojo
#RestController
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(#PathVariable String id) {
/// Some code
}
}
I tried doing adding validation to the path variable but its still not working
#RestController
#Validated
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(
#Valid
#Nonnull
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable String id) {
/// Some code
}
}
You need to create a bean in your Spring configuration:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
You should leave the #Validated annotation on your controller.
And you need an Exceptionhandler in your MyController class to handle theConstraintViolationException :
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
After those changes you should see your message when the validation hits.
P.S.: I just tried it with your #Size validation.
To archive this goal I have apply this workaround for getting a response message equals to a real Validator:
#GetMapping("/check/email/{email:" + Constants.LOGIN_REGEX + "}")
#Timed
public ResponseEntity isValidEmail(#Email #PathVariable(value = "email") String email) {
return userService.getUserByEmail(email).map(user -> {
Problem problem = Problem.builder()
.withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
.withTitle("Method argument not valid")
.withStatus(Status.BAD_REQUEST)
.with("message", ErrorConstants.ERR_VALIDATION)
.with("fieldErrors", Arrays.asList(new FieldErrorVM("", "isValidEmail.email", "not unique")))
.build();
return new ResponseEntity(problem, HttpStatus.BAD_REQUEST);
}).orElse(
new ResponseEntity(new UtilsValidatorResponse(EMAIL_VALIDA), HttpStatus.OK)
);
}

How to perform Spring validation in MultiActionController?

How to perform Spring validation in MultiActionController?
Let's write the following one
public class Person {
private String name;
private Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And your MultiActionController
import static org.springframework.validation.ValidationUtils.*;
#Component
public class PersonController extends MultiActionController {
public PersonController() {
setMethodNameResolver(new InternalPathMethodNameResolver());
setValidators(new Validator[] {new Validator() {
public boolean supports(Class clazz) {
return clazz.isAssignableFrom(Person.class);
}
public void validate(Object command, Errors errors) {
rejectIfEmpty(errors, "age", "", "Age is required");
rejectIfEmptyOrWhitespace(errors, "name", "", "Name is required");
}
}});
}
public ModelAndView add(HttpServletRequest request, HttpServletResponse response, Person person) throws Exception {
// do something (save our Person object, for instance)
return new ModelAndView();
}
}
MultiActionController defines a property called validators where you should provide any Validator used by your MultiActionController. Here you can see a piece of code which is responsible for validating your Command object inside MultiActionController
ServletRequestDataBinder binder = ...
if (this.validators != null)
for (int i = 0; i < this.validators.length; i++) {
if (this.validators[i].supports(command.getClass())) {
ValidationUtils.invokeValidator(this.validators[i], command, binder.getBindingResult());
}
}
}
/**
* Notice closeNoCatch method
*/
binder.closeNoCatch();
closeNoCatch method says
Treats errors as fatal
So if your Validator returns any Error, closeNoCatch will throw a ServletRequestBindingException. But, you can catch it inside your MultiActionController method, as follows
public ModelAndView hanldeBindException(HttpServletRequest request, HttpServletResponse response, ServletRequestBindingException bindingException) {
// do what you want right here
BindException bindException = (BindException) bindingException.getRootCause();
return new ModelAndView("personValidatorView").addAllObjects(bindException.getModel());
}
In order to test, let's do the following one
#Test
public void failureValidation() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("POST");
request.setRequestURI("http://127.0.0.1:8080/myContext/person/add.html");
/**
* Empty values
*/
request.addParameter("name", "");
request.addParameter("age", "");
PersonController personController = new PersonController();
ModelAndView mav = personController.handleRequest(request, new MockHttpServletResponse());
BindingResult bindingResult = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + "command");
/**
* Our Validator rejected 2 Error
*/
assertTrue(bindingResult.getErrorCount() == 2);
for (Object object : bindingResult.getAllErrors()) {
if(object instanceof FieldError) {
FieldError fieldError = (FieldError) object;
System.out.println(fieldError.getField());
}
}
}

Resources