I am trying to write a junit for a spring controller whose signature is something like this
#RequestMapping(value = { "/addPharmcyInLookUpTable.form" }, method = { org.springframework.web.bind.annotation.RequestMethod.POST })
public String processSubmitAddPhl(#ModelAttribute PhrmcyAdmin phrmcyAdmin,
BindingResult result, SessionStatus status,
HttpServletRequest request) throws Exception {
.....
....
}
The junit for this is
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/applicationContext.xml",
"classpath:/puela-app-config.xml" }, inheritLocations = true)
public class AddPharmacyInLookUpTableControllerTest {
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(
AddPharmacyInLookUpTableControllerTest.class);
}
#InjectMocks
private AddPharmacyInLookUpTableController controller;
private static MockHttpServletRequest request;
private static MockHttpServletResponse response;
#Autowired
private HandlerMapping handlerMapping;
#Autowired
private HandlerAdapter handlerAdapter;
#BeforeClass
public static void runBeforeAllTest() throws Exception {
System.out.println("Running one time Setup");
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
private ModelAndView handle(final HttpServletRequest request,
final HttpServletResponse response) throws Exception {
final HandlerExecutionChain handler = handlerMapping
.getHandler(request);
Assert.assertNotNull(
"No handler found for request, check you request mapping",
handler);
final Object controller = handler.getHandler();
for (final HandlerInterceptor interceptor : handlerMapping.getHandler(
request).getInterceptors()) {
if (!interceptor.preHandle(request, response, controller)) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
}
#Test
public void processRequestAddPhl_post() throws Exception
{
PhrmcyAdmin phrmcyAdmin = new PhrmcyAdmin();
phrmcyAdmin.setPhlCalMailbox("Test");
phrmcyAdmin.setPhlMailPharmacy("FootHill");
request.setMethod("POST");
request.setRequestURI("/addPharmcyInLookUpTable.form");
// Code goes here
MockHttpSession session = new MockHttpSession();
ModelAndView mv = handle(request, response);
assertEquals(mv.getViewName(), "addPhrmcyInTable.view");
}
}
I am trying to send this model object phrmcyAdmin along with the request. Any idea how we can deal with the model object??
Related
I have a spring boot app(v2.3.0.RELEASE) and I need to get any request being sent from my restcontroller when there is a bean validation error.
My Request is as follows:
public class PaymentRequest {
#Valid
private PaymentIdentificationRequest paymentIdentification;
#NotBlank(message = "transactionTypeCode.required")
private String transactionTypeCode;
#NotBlank(message = "name.required")
private String name;
}
For instance, if name is null, I need an interceptor to capture values of transactionTypeCode and
paymentIdentification before exception is triggered.
I tried implementing the following interceptor to capture all not null parameters value being sent:
public class MyInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception {
//capture required valued set it in HttpServletRequest attribute to be used for exception handling
HandlerMethod h1 = (HandlerMethod) handler;
MethodParameter[] param = null;
System.out.println("MINIMAL: INTERCEPTOR PREHANDLE CALLED");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
Enumeration<?> e = request.getParameterNames();
System.out.println("MINIMAL: INTERCEPTOR POSTHANDLE CALLED");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
Enumeration<?> e = request.getParameterNames();
System.out.println("MINIMAL: INTERCEPTOR AFTERCOMPLETION CALLED");
}
When the name is null it enters the method preHandle but I am not able to get the parameters and its corresponding values being sent, any idea how to do it pls?
The reason why I am doing the above changes is because I need to set the value of transactionTypeCode and paymentIdentification in my interceptor above so as to use them below in my exception handler as follows:
#ControllerAdvice
public class RestControllerExceptionHandler extends ResponseEntityExceptionHandler {
private #Autowired
HttpServletRequest httpServletRequest;
#Override
public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception, HttpHeaders headers,
HttpStatus status, WebRequest request) {
log.error(exception.getMessage(), exception);
// mapParam is size zero
Map<String, String[]> mapParam = httpServletRequest.getParameterMap();
if (!ObjectUtils.isEmpty(exception) && !ObjectUtils.isEmpty(this.request1)) {
paymentValidator.onErrorUpdatePayment(this.request1.getAttribute("transactionTypeCode"), this.request1.getAttribute("paymentIdentification "), exception.toString());
}
....
return new ResponseEntity<>(ipsResponse, new HttpHeaders(), ipsResponse.getHttpStatus());
}
I have created a Spring Restful Service and Spring MVC application.
Restful Service ::
Restful service returns an entity if its existing in DB. If it doesn't exist It returns a custom Exception information in ResponseEntity object.
It is working as expected tested using Postman.
#GetMapping(value = "/validate/{itemId}", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public ResponseEntity<MyItem> validateItem(#PathVariable Long itemId, #RequestHeader HttpHeaders httpHeaders) {
MyItem myItem = myitemService.validateMyItem(itemId);
ResponseEntity<MyItem> responseEntity = null;
if (myItem == null) {
throw new ItemNotFoundException("Item Not Found!!!!");
}
responseEntity = new ResponseEntity<MyItem>(myItem, headers, HttpStatus.OK);
return responseEntity;
}
If the requested Entity does not exist Restful Service returns below.
#ExceptionHandler(ItemNotFoundException.class)
public ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {
System.out.println("In CREEH::ItemNFE");
ExceptionResponse exceptionResponse = new ExceptionResponse("Item Not Found Ex!!!", new Date(), webRequest.getDescription(false));
ResponseEntity<ExceptionResponse> responseEntity = new ResponseEntity<ExceptionResponse>(exceptionResponse, HttpStatus.NOT_FOUND);
return responseEntity;
}
But when I am calling the above service from a spring MVC application using RestTemplate, It is returning a valid object if it exists.
If the requested object does not exist Restful service is returning the exception information but its not reaching the calling(spring MVC) application.
Spring MVC application calls Restful Web Service using Rest template
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
ResponseEntity<Object> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Object.class, uriParms);
int restCallStateCode = responseEntity.getStatusCodeValue();
This is expected behavior. Rest template throws exception when the http status is client error or server error and returns the response when http status is not error status.
You have to provide implementation to use your error handler, map the response to response entity and throw the exception.
Create new error exception class with ResponseEntity field.
public class ResponseEntityErrorException extends RuntimeException {
private ResponseEntity<ErrorResponse> errorResponse;
public ResponseEntityErrorException(ResponseEntity<ErrorResponse> errorResponse) {
this.errorResponse = errorResponse;
}
public ResponseEntity<ErrorResponse> getErrorResponse() {
return errorResponse;
}
}
Custom error handler which maps the error response back to ResponseEntity.
public class ResponseEntityErrorHandler implements ResponseErrorHandler {
private List<HttpMessageConverter<?>> messageConverters;
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return hasError(response.getStatusCode());
}
protected boolean hasError(HttpStatus statusCode) {
return (statusCode.is4xxClientError() || statusCode.is5xxServerError());
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpMessageConverterExtractor<ExceptionResponse> errorMessageExtractor =
new HttpMessageConverterExtractor(ExceptionResponse.class, messageConverters);
ExceptionResponse errorObject = errorMessageExtractor.extractData(response);
throw new ResponseEntityErrorException(ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(errorObject));
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
}
RestTemplate Configuration - You have to set RestTemplate's errorHandler to ResponseEntityErrorHandler.
#Configuration
public class RestTemplateConfiguration {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntityErrorHandler errorHandler = new ResponseEntityErrorHandler();
errorHandler.setMessageConverters(restTemplate.getMessageConverters());
restTemplate.setErrorHandler(errorHandler);
return restTemplate;
}
}
Calling Method
#Autowired restTemplate
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
try {
ResponseEntity<Object> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Object.class, uriParms);
int restCallStateCode = responseEntity.getStatusCodeValue();
} catch (ResponseEntityErrorException re) {
ResponseEntity<ErrorResponse> errorResponse = re.getErrorResponse();
}
Try using the #ResponseBody annotation on your Exceptionhandler. e.g:
public #ResponseBody ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {... }
You should use Custom Exception Handler to fix your case. It looks like this
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public CustomResponseEntityExceptionHandler() {
super();
}
// 404
#ExceptionHandler(value = { EntityNotFoundException.class, ResourceNotFoundException.class })
protected ResponseEntity<Object> handleNotFound(final RuntimeException ex, final WebRequest request) {
BaseResponse responseError = new BaseResponse(HttpStatus.NOT_FOUND.value(),HttpStatus.NOT_FOUND.name(),
Constants.HttpStatusMsg.ERROR_NOT_FOUND);
logger.error(ex.getMessage());
return handleExceptionInternal(ex, responseError, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
}
And your code should throw some exception, eg:
if (your_entity == null) {
throw new EntityNotFoundException("said something");
}
If you get this case in somewhere else again, you just throw exception like above. Your handler will take care the rest stuffs.
Hope this help.
I've started your application and works just fine.
Maven :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
The controller class is :
#Controller
public class ValidationController {
#GetMapping(value = "/validate/{itemId}")
public #ResponseBody ResponseEntity<MyItem> validateItem(#PathVariable Long itemId) {
if (itemId.equals(Long.valueOf(1))) {
throw new ItemNotFoundException();
}
return new ResponseEntity<>(new MyItem(), HttpStatus.OK);
}
#ExceptionHandler(ItemNotFoundException.class)
public ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {
System.out.println("In CREEH::ItemNFE");
ExceptionResponse exceptionResponse = new ExceptionResponse("Item Not Found Ex!!!", new Date(), webRequest.getDescription(false));
ResponseEntity<ExceptionResponse> responseEntity = new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
return responseEntity;
}
}
and the test:
#RunWith(SpringRunner.class)
#WebMvcTest(value = ValidationController.class, secure = false)
public class TestValidationController {
#Autowired
private MockMvc mockMvc;
#Test
public void testExpectNotFound() throws Exception {
mockMvc.perform(get("/validate/1"))
.andExpect(status().isNotFound());
}
#Test
public void testExpectFound() throws Exception {
mockMvc.perform(get("/validate/2"))
.andExpect(status().isOk());
}
}
Are you sure the url you are trying to use with RestTemplate is correct?
String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
Your get method is #GetMapping(value = "/validate/{itemId}"
If you don't have request mapping at the level of the controller the url should be:
http://localhost:8080/validate/1
Another difference is the missing #ResponseBody on your controller method.
I try to test my security layer using MockMvc. I've write the following integration test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ApplicationContextConfig.class,WebSecurityConfig.class})
#WebAppConfiguration
public class AuthenticationStatesIT {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void initMocks(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(new AuthenticationFilter(), "/*")
.build();
}
#Test
public void stage10_firstRequestForLoginPageShouldReturnProperPageAndAddUnauthenticatedStateToSession () throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/"))
.andDo(print())
//.andExpect(cookie().exists("JSESSIONID"))
.andExpect(status().is3xxRedirection()).andReturn();
MockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession();
StatesAuthenticator authenticator = (StatesAuthenticator)session.getAttribute("authenticator");
AuthenticationState state = authenticator.getState();
Assert.assertNotNull(authenticator);
Assert.assertNotNull(state);
}
}
Everything work ok except one detail. The 'JSESSIONID' cookie is not creating. I am sure that the new session is created but the test 'andExpect(cookie().exists("JSESSIONID"))' is not passed. I am creating session as follows:
public class UnauthenticatedState implements AuthenticationState {
#Override
public void doAuthentication(StatesAuthenticator authentication,ServletRequest request,
ServletResponse response,FilterChain chain) throws IOException, ServletException {
authentication.setAuthentication(null);
HttpServletResponse httpResponse = (HttpServletResponse)response;
HttpServletRequest httpRequest = (HttpServletRequest)request;
//get the old session and invalidate if exists
HttpSession oldSession = httpRequest.getSession(false);
if (oldSession != null) {
oldSession.invalidate();
}
//generate a new session
HttpSession session = httpRequest.getSession(true);
session.setMaxInactiveInterval(300); // 5 minutes
session.setAttribute("authenticator", authentication);
authentication.setState(new AuthenticatingState());
httpResponse.sendRedirect("login");
}
}
When I run server and look for that cookie in browser everything is ok, the cookie exists. Can someone explain me why MockMvc do no set 'JSESSIONID'? Thanks for any help!
I have a really simple controller defined in this way:
#RequestMapping(value = "/api/test", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody Object getObject(HttpServletRequest req, HttpServletResponse res) {
Object userId = req.getAttribute("userId");
if (userId == null){
res.setStatus(HttpStatus.BAD_REQUEST.value());
}
[....]
}
I tried to call using MockMvc in many different way but, I'm not able to provide the attribute "userId".
For instance, with this it doesn't work:
MockHttpSession mockHttpSession = new MockHttpSession();
mockHttpSession.setAttribute("userId", "TESTUSER");
mockMvc.perform(get("/api/test").session(mockHttpSession)).andExpect(status().is(200)).andReturn();
I also tried this, but without success:
MvcResult result = mockMvc.perform(get("/api/test").with(new RequestPostProcessor() {
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
request.setParameter("userId", "testUserId");
request.setRemoteUser("TESTUSER");
return request;
}
})).andExpect(status().is(200)).andReturn();
In this case, I can set the RemoteUser but never the Attributes map on HttpServletRequest.
Any clue?
You add a request attribute by calling requestAttr ^^
mockMvc.perform(get("/api/test").requestAttr("userId", "testUserId")...
You could use
mvc.perform(post("/api/v1/...")
.with(request -> {
request.addHeader(HEADER_USERNAME_KEY, approver);
request.setAttribute("attrName", "attrValue");
return request;
})
.contentType(MediaType.APPLICATION_JSON)...
#ResponseStatus(HttpStatus.OK)
#GetMapping(Routes.VALIDATE_EMAIL_TOKEN + "/validate")
public String validateEmailToken(#RequestParam(value = "token") String token,
HttpServletRequest httpServletRequest) throws RestServiceException {
return credentionChangeService.getUserByToken(token, httpServletRequest);
}
//test method
#Mock
private HttpServletRequest httpServletRequest
#Mock
private MerchantCredentialsChangeService mockCredentionChangeService;
#Test
public void testValidateEmailToken() throws Exception {
final String token = "akfkldakkadjfiafkakflkd";
final String expectedUsername = "9841414141";
Mockito.when(mockCredentionChangeService.getUserByToken(Matchers.eq(token), Matchers.any(HttpServletRequest.class)))
.thenReturn(expectedUsername);
mockMvc.perform(get(Routes.VALIDATE_EMAIL_TOKEN + "/validate")
.param("token", token))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string(expectedUsername));
}
java.lang.NoSuchMethodError:
org.junit.runner.notification.RunNotifier.testAborted(Lorg/junit/
runner/Description;Ljava/lang/Throwable;)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMĀethod(SpringJUnit4ClassRunner.java:
155)
And written testcase for controller like, newly writing testcases for Spring Controller classes:
TestXController.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"file:D:/ABC/src/main/webapp/WEB-INF/
xyz-servlet.xml",
"file:D:/ABC/src/main/webapp/WEB-INF/xyzrest-servlet.xml"})
public class TestXController {
#Inject
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
private XController controller;
#Test
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
// I could get the controller from the context here
controller = new XController();
}
#Test
public void testgoLoginPage() throws Exception {
request.setAttribute("login", "0");
final org.springframework.web.servlet.ModelAndView mav = handlerAdapter.handle(request, response, controller);
assertViewName(mav, null);
assertAndReturnModelAttributeOfType(mav, "login", null);
}
#Test
public void testgoHomePage(){
org.springframework.web.servlet.ModelAndView mav =null;
request.setAttribute("success1", "1");
request.setAttribute("success", "1");
try {
mav = handlerAdapter.handle(request, response, controller);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
assertViewName(mav, null);
assertAndReturnModelAttributeOfType(mav, "home",null);
}
Can any one Guide me on this to write test cases for Spring
Controller classes,Or any code samples links.
Thanks & Regards, Venu Gopala Reddy.
Yes, make sure you're using the right version of JUnit. I think there's a mismatch with the Spring test JAR that forces you to use JUnit 4.4.