My problem is to how to call this. I could do
MyObject o = new MyObject();
myController.save(o, "value");
but this is not what I would like to do. I would like the MyObject to be in the request post body? How can this be done?
#Requestmapping(value="/save/{value}", method=RequestMethod.POST)
public void post(#Valid MyObject o, #PathVariable String value{
objectService.save(o);
}
Just to be clear I am talking about unit testing.
Edit:
#RequestMapping(value = "/", method = RequestMethod.POST)
public View postUser(ModelMap data, #Valid Profile profile, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return dummyDataView;
}
data.put(DummyDataView.DATA_TO_SEND, "users/user-1.json");
profileService.save(profile);
return dummyDataView;
}
See sample code below that demonstrates unit testing a controller using junit and spring-test.
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
#Transactional
#ContextConfiguration(locations = {
"classpath:rest.xml"
})
public class ControllerTest{
private MockHttpServletRequest request;
private MockHttpServletResponse response;
#Autowired
private RequestMappingHandlerAdapter handlerAdapter;
#Autowired
private RequestMappingHandlerMapping handlerMapping;
#Before
public void setUp() throws Exception
{
this.request = new MockHttpServletRequest();
request.setContentType("application/json");
this.response = new MockHttpServletResponse();
}
#Test
public void testPost(){
request.setMethod("POST");
request.setRequestURI("/save/test"); //replace test with any value
final ModelAndView mav;
Object handler;
try{
MyObject o = new MyObject();
//set values
//Assuming the controller consumes json
ObjectMapper mapper = new ObjectMapper();
//set o converted as JSON to the request body
//request.setContent(mapper.writeValueAsString(o).getBytes());
request.setAttribute("attribute_name", o); //in case you are trying to set a model attribute.
handler = handlerMapping.getHandler(request).getHandler();
mav = handlerAdapter.handle(request, response, handler);
Assert.assertEquals(200, response.getStatus());
//Assert other conditions.
}
catch (Exception e)
{
}
}
}
You need to use RequestBody:
#Requestmapping(value="/save/{value}", method=RequestMethod.POST)
public void post(#RequestBody MyObject o, #PathVariable String value{
objectService.save(o);
}
general info about request body documentation : http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html#mvc-ann-requestbody
Related
I am doing my integration test using test rest template but for postForEntity method I am getting null as response body, but I saw examples which were working fine on that but I am unable to resolve my issue,
PFB my test case
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class PersonControllerTests {
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private PersonRepository mockRepository;
#Before
public void init() {
List<Person> list = new ArrayList<>();
Person p1 = new Person("dumm1", "lastName1",22);
Person p2 = new Person("dumm2", "lastName2",32);
p1.setId(1l);
list.add(p2);
list.add(p1);
when(mockRepository.findAll()).thenReturn(list);
when(mockRepository.findById(1l)).thenReturn(Optional.of( p1 ));
}
#Test
public void createPerson() throws Exception {
Person p = new Person("dummy1", "dumm2", 11);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Person> httpEntity = new HttpEntity<Person>(p,headers);
ResponseEntity<String> response = restTemplate
.withBasicAuth("user", "password")
.postForEntity("/persons/create", httpEntity, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
//assertEquals(MediaType.APPLICATION_JSON, response.getHeaders().getContentType());
assertEquals(11, response.getBody());
}
PFB my original code:
#RestController
#RequestMapping("/persons")
public class PersonController {
#Autowired
PersonRepository personRepository;
#GetMapping("/all")
public Iterable<Person> getAllUser() {
return personRepository.findAll();
}
#PostMapping(value ="/create",consumes = MediaType.APPLICATION_JSON_VALUE)
public Person createUser( #RequestBody Person person) {
return personRepository.save(person);
}
}
I believe I am doing some silly mistake but unable to understand
You are using #MockBean for your PersonRepository. Without any stubbing setup, Mockito returns null for reference types.
That's why return personRepository.save(person); returns null during your test.
You can stub your mock to simulate its behavior:
// import static org.mockito.ArgumentMatchers.any;
when(personRepository.save(ArgumentMatchers.any(Person.class))).thenAnswer(invocation -> {
Person person = invocation.getArgument(0);
// I assume you have an id field
person.setId(42L);
return person;
});
Hi I'm trying to write some tests with Mockito and PowerMockito (I need to mock private methods) for a rest service written with SpringMVC and I'm facing the following issue
This is the semplified version of the controller
#Controller
#RequestMapping(value = "/test")
public class SimpleController {
#Autowired
private HttpServletRequest httpRequest;
#RequestMapping(value = "/simpleservice", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
#ResponseBody
public SimpleServiceResponse simpleService(#RequestBody SimpleServiceRequest simpleServiceRequest, HttpServletRequest httpServletRequest) {
SimpleServiceResponse simpleServiceResponse=new SimpleServiceResponse(simpleServiceRequest.getValue());
httpRequest.getHeader("Header");
return simpleServiceResponse;
}
}
and this is the correspoding test class
#WebAppConfiguration
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:WebApplicationContext.xml","classpath:SimpleApplicationContext.xml"})
#PrepareForTest(WebController.class)
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class TestSimpleControllerMockito {
private Logger logger = LoggerFactory.getLogger(TestSimpleControllerMockito.class.getName());
private ObjectMapper objectMapper= new ObjectMapper();
#InjectMocks
private SimpleController controller;
#Test
public void testSimpleService() throws Exception {
MockitoAnnotations.initMocks(this);
SimpleService mockedSimple = mock(SimpleService.class);
when(mockedSimple.doSimpleService(any(SimpleServiceRequest.class))).thenReturn(new SimpleServiceResponse("MockMock"));
SimpleController mockedController=PowerMockito.spy(controller);
SimpleServiceRequest simpleServiceRequest= new SimpleServiceRequest("ciao");
String requestAsStr=objectMapper.writeValueAsString(simpleServiceRequest);
MockMvc mMockMvc=MockMvcBuilders.standaloneSetup(mockedController).build();
MvcResult result= mMockMvc.perform(post("/test/simpleservice").content(requestAsStr).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
String content = result.getResponse().getContentAsString();
SimpleServiceResponse simpleServiceResponse=objectMapper.readValue(content,SimpleServiceResponse.class);
Assert.assertEquals("MockMockMockedSessionManager",simpleServiceResponse.getValue());
}
}
When running the test case I got a NullPointerEception on httpRequest.getHeader("Header");
My guess is that using the #InjectMocks annotation and then using
SimpleController mockedController=PowerMockito.spy(controller);
is the cause of the NullPointerException. I don't know how to preserve the #Autowire annotation processing on the controller Object. I already found a workaround, but it requires to write some redundant code.
Is there a way to make the #autowired annotation work?
Thanks a lot.
P.S.
the SimpleServiceXXX classes are like this one:
public class SimpleServiceResponse {
private String value;
public SimpleServiceResponse() {
}
public SimpleServiceResponse(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
I have some controller's method:
#RequestMapping("/")
#AuthorizedRNUser
public Object index(UserStateVO userStateVO) {
return userStateVO;
}
Also I have HandlerMethodArgumentResolver for UserStateVO parameter
public class UserStateArgumentHandlerResovler implements HandlerMethodArgumentResolver{
#Autowired
RNService service;
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getMethod().isAnnotationPresent(AuthorizedRNUser.class) && methodParameter.getParameterType() == UserStateVO.class;
}
#Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
UserStateVO userState = service.getUserState();
if (isNull(userState))
// here i need to return 403 HTTP response
throw new RuntimeException("User is not allowed");
return userState;
}
}
And if the UserStateVO is null I need to return 403 HTTP response, but I do not know is it possible? How best to check UserStateVO and pass it into a controller or return HTTP response?
Use the same method as handling exceptions in MVC exception-handling-in-spring-mvc
Add your custom exception, e.g
public class BadRequestException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BadRequestException(String message) {
super(message);
}
}
And either annotate it with #ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "User is not allowed") or add #ControllerAdvice class with method like
#ExceptionHandler(value = { BadRequestException.class })
#ResponseStatus(value = HttpStatus.FORBIDDEN)
#ResponseBody
public Map<String, String> handleBadRequestException(BadRequestException e) {
Map<String, String> retMessages = new HashMap<>();
retMessages.put("message", e.getMessage());
return retMessages;
}
what remains is just to throw it
if (isNull(userState))
// here i need to return 403 HTTP response
throw new BadRequestException("User is not allowed");
I want to handle exception for both normal and rest/ajax requests. Here is my code,
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
public ModelAndView handleCustomException(Exception ex) {
ModelAndView model = new ModelAndView("error");
model.addObject("errMsg", ex.getMessage());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
sw.toString();
model.addObject("errTrace", sw);
return model;
}
#ExceptionHandler(Exception.class)
#ResponseBody
public String handleAjaxException(Exception ex) {
JSONObject model = new JSONObject();
model.put("status", "error");
model.put("errMsg", ex.getMessage());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
sw.toString();
model.put("errTrace", sw);
return model.toString();
}
}
This will give me an error as I cant have #ExceptionHandler(Exception.class) twice. So what could be the solution?
see the configuration of #ControllerAdvice:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html
So you can create two classes(error handlers) and specify annotations/basePackages/assignibaleTypes
For example for REST(ajax) use #RestController annotation for your controllers and you can handle errors like this:
#ControllerAdvice(annotations = RestController.class)
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
#ResponseBody
public String handleAjaxException(Exception ex) {
...
}
}
for other cases it can be error handler with annotation
#ControllerAdvice(annotations = Controller.class)
This is global exception handler in spring mvc.this is called every time when exception found in your application.I think you to control only 404 exception with the help of web.xml.
#ControllerAdvice
public class GlobalExceptionController {
#ExceptionHandler(Throwable.class)
#ResponseBody
public ModelAndView handleAllException(Throwable ex,
HttpServletResponse response) {
ex.printStackTrace();
// Set Status
response.setStatus(500);
// Set View
ModelAndView model = new ModelAndView("500");
model.addObject("navlabel", "");
model.addObject("userActivity", new ArrayList<String>());
// Set exception Message
model.addObject("errMsg", ex.getMessage());
return model;
}
}
You can create inner static class #RestControllerAdvice. Noot needed to create separated #RestController for this.
#ControllerAdvice
public class BaseController {
private static final Logger logger =
LoggerFactory.getLogger(BaseController.class);
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleException(Exception error, Model model) {
logger.error("Error was: " + error.getMessage(), error);
model.addAttribute("message", error.getMessage());
model.addAttribute("stackTrace", error.getStackTrace());
model.addAttribute("exception", error);
return "error"; //return view
}
#RestControllerAdvice
public static class RestBaseController {
private static final Logger logger = LoggerFactory.getLogger(RestBaseController.class);
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleException(Exception error) {
logger.error("Error was: " + error.getMessage(), error);
return "error"; //return "error"
}
}
}
You can write two exception handler to handle both normal and rest/ajax requests exception. Here is sample code to illustrate the solution.
#ControllerAdvice(annotations = RestController.class)
#Order(1)
class RestExceptionHandler {
#ExceptionHandler(MyException.class)
#ResponseBody
ResponseEntity<ErrorResponse> exceptionHandler() {
....
}
}
#ControllerAdvice(annotations = Controller.class)
#Order(2)
class ExceptionHandler {
#ExceptionHandler(Exception.class)
public ModelAndView handleError500(HttpServletRequest request, HttpServletResponse response, Exception ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("error", "500");
return mav;
}
}
Is is possible to modify a #ModelAttribute before it is validated via #Validated.
ie
#RequestMapping(value = "/doSomething", method = RequestMethod.POST)
public final ModelAndView save(
#Validated(value = {myGroup.class}) #ModelAttribute("myObject") MyObject myObject)
I need to change the state of myObject before #Validated is executed
What about add a ModelAttribute populate method?
#ModelAttribute("myObject")
public MyObject modifyBeforeValidate(
#ModelAttribute("myObject") MyObject myObject) {
//modify it here
return myObject;
}
The side affect is this method will be invoked before every #RequestMapping method if I'm not mistaken.
Update1: example
#ModelAttribute("command")
public ChangeOrderCommand fillinUser(
#ModelAttribute("command") ChangeOrderCommand command,
HttpServletRequest request) {
command.setUser(securityGateway.getUserFrom(request));
return command;
}
#RequestMapping(value = "/foo/bar", method = RequestMethod.POST)
public String change(#ModelAttribute("command") ChangeOrderCommand command,
BindingResult bindingResult, Model model, Locale locale) {
}
There are 2 ways to modify the model attribute object before the #Validated will trigger:
Remove #Validated and autowire the validator and manually trigger the validator:
class MyController {
private final Validator validator;
class MyController(Validator validator) {
this.validator = validator;
}
#PostMapping("/doSomething")
public final ModelAndView save(
#ModelAttribute("myObject") MyObject myObject, BindingResult result) {
// edit MyObject here
validator.validate(myObject, result)
// original method body here
}
Decorate the default validator and pre-process the myObject object inside the decorated validator.
class MyController {
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setValidator(new PreProcessMyObjectValidator(binder.getValidator()));
}
#PostMapping("/doSomething")
public final ModelAndView save(
#Validated(value = {myGroup.class}) #ModelAttribute("myObject") MyObject myObject, BindingResult result) {
...
}
private static class PreProcessMyObjectValidator implements Validator {
private final Validator validator;
public PreProcessMyObjectValidator(Validator validator) {
this.validator = validator;
}
#Override
public boolean supports(#Nonnull Class<?> clazz) {
return validator.supports(clazz);
}
#Override
public void validate(#Nonnull Object target, #Nonnull Errors errors) {
if (target instanceof MyObject) {
MyObject myObject = (MyObject) target;
// manipulate myObject here
}
validator.validate(target, errors);
}
}
}
(This second tip is what I picked up from https://github.com/spring-projects/spring-framework/issues/11103)