Mocking postForObject using PowerMockito throws error - resttemplate

I'm trying to mock RestTemplates postForObject method using PowerMockito.
PowerMockito.when(mockRestTemplate, "postForObject",
Mockito.eq(URI.class), Mockito.eq(Object.class), Mockito.eq(Class.class))
.thenReturn(mockResponse);
This is throwing a java.lang.IllegalArgumentException below
java.lang.IllegalArgumentException: 'responseType' must not be null
I gather it needs to meet a notNull constraint on the postForObject's method's responseType parameter. But I am not able to figure out how to mention this.
public <T> T postForObject(URI url, #Nullable Object request, Class<T> responseType)

Related

Combine #WebMcvTest with #PathVariable annotated controllers in Spring Boot 1.41

Sample application for this question is here: https://github.com/olemerdy-fa/webmvctest
I am bootstrapping a new project using Spring Boot 1.4.1. I try to leverage the new features from this great framework, especially the ability to (quite-)unit test 'slices' of my application.
I am now struggled with the #WebMvcTest feature when used on a #Controller declaring a #PathVariable annotated method.
Indeed, a #WebMvcTest is supposed to bootstrap a single controller and the MockMvc testing facility, without providing anything else. Using #MockBean, it's still quite easy to provide mocks as dependencies to inject inside this controller.
But what about a #PathVariable annotated parameter whose type is, say, a JPA Entity whose converter is usually registered by Spring Data?
The sample project joined to this question contains a few samples:
MyEntity is a simple JPA entity and MyEntityRepository its Spring Data associated repository
Webmvctest1Controller has a load method retrieving the id from the path and calls itself the MyEntityRepository.findOne(id) method
Webmvctest1ControllerUnitTest tests this controller by mocking MyEntityRepository and everything just goes well
Webmvctest2Controller has a load method with a #PathVariable annotated MyEntity which is looked up by Spring Data registered converter
#RestController
public class Webmvctest2Controller {
#RequestMapping("load2/{id}")
public MyEntity load2(#PathVariable("id") MyEntity myEntity) {
return myEntity;
}
}
Webmvctest2ControllerUnitTest is where I'm stuck, as I do not know how to provide a mock entity as the parameter while still using MockMvc
#RunWith(SpringRunner.class)
#WebMvcTest(Webmvctest2Controller.class)
public class Webmvctest2ControllerUnitTest {
#Autowired
private MockMvc mvc;
#Test
public void load2() throws Exception {
// How do I mock converter to PathVariable here?
mvc.perform(get("/load2/123").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{id:123,name:'My Entity 123'}"));
}
}
This fails with a org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException exception
2016-10-25 14:27:55.699 WARN 20753 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to convert request element: org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException: Failed to convert value of type [java.lang.String] to required type [com.stackoverflow.MyEntity]; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.stackoverflow.MyEntity]: no matching editors or conversion strategy found
MockHttpServletRequest:
HTTP Method = GET
Request URI = /load2/123
Parameters = {}
Headers = {Accept=[application/json]}
Handler:
Type = com.stackoverflow.Webmvctest2Controller
Method = public com.stackoverflow.MyEntity com.stackoverflow.Webmvctest2Controller.load2(com.stackoverflow.MyEntity)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException
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 = []
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.375 sec <<< FAILURE! - in com.stackoverflow.Webmvctest2ControllerUnitTest
load2(com.stackoverflow.Webmvctest2ControllerUnitTest) Time elapsed: 0.015 sec <<< FAILURE!
java.lang.AssertionError: Status expected:<200> but was:<500>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:54)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:81)
at org.springframework.test.web.servlet.result.StatusResultMatchers$10.match(StatusResultMatchers.java:664)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
at com.stackoverflow.Webmvctest2ControllerUnitTest.load2(Webmvctest2ControllerUnitTest.java:28)
WebmvctestApplicationTests shows that everything is fine in both cases when the application is fully bootstrapped
Any idea of how I could keep my #PathVariable entity parameters while still only testing my web slice with #WebMvcTest?
Thanks :)
Thanks to #zeroflagL hinting about possible contributions to HandlerMethodArgumentResolver, I've come up with a solution that seems to fit my need. Contributing a custom Converter handling the mock entity injection in the controller seems to do the job
#RunWith(SpringRunner.class)
#WebMvcTest(Webmvctest2Controller.class)
public class Webmvctest2ControllerUnitTest {
#Autowired
private MockMvc mvc;
#Test
public void load2() throws Exception {
mvc.perform(get("/load2/123").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{id:123,name:'My Entity'}"));
}
#TestConfiguration
static class InternalConfig {
#Bean
WebMvcConfigurer configurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(String.class, MyEntity.class, id -> {
if (id.equals("123")) {
MyEntity myEntity = new MyEntity(123);
myEntity.setName("My Entity");
return myEntity;
}
throw new IllegalArgumentException();
});
}
};
}
}
}
It is not perfect, as the mock Entity cannot be provided by the test method itself but it is still allows the web-unit-test bootstrap and run I wanted to keep.

Cannot pass #SortDefault Sort object into controller method?

It is stated in the documentation of #SortDefault that
Annotation to define the default Sort options to be used when
injecting a Sort instance into a controller handler method.
But the fact is, I got this exception:
Failed to instantiate [org.springframework.data.domain.Sort]: No
default constructor found; nested exception is
java.lang.NoSuchMethodException:
org.springframework.data.domain.Sort.()
Did I miss something here?
void download(WebRequest request, HttpSession session,
#RequestParam(value = "fields",
defaultValue = "id,hostname,networkId,customerId") String[] visibleProperties,
#SortDefault("hostname") Sort sort, HttpServletResponse response) {
}
You probably missed an #EnableSpringDataWebSupport in your configuration.

Testing with autowired dependency like session in Validator

I have few custom annotations defined on fields of an object like:
public class Person{
#Accountname
String email;
}
Implementation class of #Accountname:
#Autowired ValidationService service;
#Autowired ClientSession clientSession;
#Override
public boolean isValid(final String email, final ConstraintValidatorContext ctx) {
if(!service.isAccountValid(email, clientSession)){
return false;
}
}
I am trying to write junits for these annotations.
#Test
public void validEmailTest()
{
person.setEmail("abc#xyz.com");
Set<ConstraintViolation<Person>> violations = validatorInstance.getValidator().validateProperty(person, "email");
Assert.assertEquals(1, violations.size());
}
But its throwing this error when I execute the test:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.clientSession': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:343)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:34)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.getTarget(CglibAopProxy.java:663)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:614)
at org.hibernate.validator.internal.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:308)
... 45 more
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:90)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:329)
... 54 more
Any idea on how to write junits if a validator class has a dependency on services like session etc.?
This should be tested separately (units).
The real logic that validates is in your ValidationService, so test it there, in AccountnameValidator test only the logic that is in there, injecting your dependencies:
#Mock ValidationService service;
#Mock ClientSession clientSession;
#InjectMocks AccountnameValidator av = new AccountnameValidator()
//initialize mocks
//example test
when(service.isAccountValid(email, clientSession)).thenReturn(true);
boolean result = av.isValid(email, ctx);
assertTrue(result);
And finally if you want you can validate presence of the annotation in Person class on email field using reflection.

Spring form binding - use IllegalArgumentException message as error message

I have a custom domain class with a single constructor that takes a String, as well as a toString() method. The constructor decodes the input string, performs validations on it and throws IllegalArgumentException if invalid.
I want to bind directly to this field, as described here: http://blog.springsource.org/2009/11/17/spring-3-type-conversion-and-validation/ (see 'Convention Over Configuration' section).
That is working fine & I am displaying the error message resolved by Spring (typeMismatch on barcodeInfo).
I know that I can customize this error message using a messageSource entry, e.g.
typeMismatch.barcodeInfo=Invalid format
However, the error message that I want to display isn't always the same, it depends on the value of the input string. Hence, I want to display the error message that I originally used in the IllegalArgumentException that I threw from the constructor. Is this possible?
I am specifically looking for a solution which will work with Spring WebFlow.
You might want to check BindingErrorProcessor used by WebDataBinder. There you can implement your own custom logic for translating exceptions to validation errors.
Notes:
You should implement your own exception (to be able to distinguish it from IllegalArgumentException thorwn by other components).
You can initialize WebDataBinder with your custom BindingErrorProcessor within your #InitBinder method (or set specific WebBindingInitializer to your handler adapter).
As Pavel mentioned in his answer, you can achieve this by implementing BindingErrorProcessor.
It should look like this:
...
import org.springframework.validation.DefaultBindingErrorProcessor;
...
#Controller
public class YourController {
...
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setBindingErrorProcessor(new DefaultBindingErrorProcessor() {
#Override
public void processPropertyAccessException(
PropertyAccessException ex, BindingResult bindingResult) {
if (ex.getPropertyName().equals("fieldInQuestion")) {
Throwable cause = ex.getMostSpecificCause();
FieldError fieldError;
fieldError = new FieldError(
bindingResult.getObjectName(),
"fieldInQuestion",
cause.getMessage());
bindingResult.addError(fieldError);
} else {
super.processPropertyAccessException(ex, bindingResult);
}
}
});
}
}

Returning an error and message from a Spring controller or service

I'm migrating some servlets over to the Spring framework, using Spring MVC. Currently in each servlet we authenticate the user and if the authentication fails we do this:
if (authfailed)
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"You are not authorized.");
return;
}
On the front end is a YUI-based application, and when an error status is returned the "failure" callback displays a dialog with the error message given above.
I know in my controller I can get the response object and call sendError, but is that the best way to handle this? sendError also throws an IOException so I'd have to catch that - a bit of annoying code to insert in every method of every controller.
I have the same problem handling exceptions - the servlets have try-catch blocks that call sendError in the catch method. I know I can mark my exception handlers with
#ExceptionHandler
#ResponseStatus(value = HttpStatus.NOT_FOUND)
but doesn't the exception handling class need to be in each controller class?
Finally, if the exception happens in a service called from a controller, does the exception bubble up to the controller or should I handle the exception in the service (thus pushing these exception handling issues into the service layer)?
This seems more difficult than it should be, but as with many things in Spring it's likely I don't understand what's going on. All I want to do is to send an error status and message back in the response!
Thanks,
Paul
It looks like you have the most of the answers in your question itself :)
To reiterate,
Have the controller like this
#RequestMapping("/test")
public String verifyAuth(HttpServletRequest request) throws NotFoundException {
String id = request.getParameter("id");
if (id == null)
throw new NotFoundException("Id not found in the request");
return "success";
}
Declare the exception class in NotFoundException.java,
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Id Not Found")
public class NotFoundException extends Exception {
public NotFoundException(String msg) {
super(msg);
}
}
This exception class need not be every controller class. Declare it as public class and import it in every required controller.
This is one way of doing it. If you like the non-spring style, declare HttpServletResponse in every controller arguments and do
#RequestMapping("/test")
public String verifyAuth(HttpServletRequest request, HttpServletResponse response) {
...
try {
response.sendError(..)
catch(..) {}
}
Or you can use views to show error message,
#RequestMapping("/test")
public String verifyAuth(HttpServletRequest request, Map<String, Object> map){
String id = request.getParameter("id");
if (id == null) {
map.put("status", HttpStatus.NOTFOUND);
map.put("reason", "Id Not Found");
return "error"
}
return "success";
}
Make sure your viewResolver is configured correctly and in the error.jsp to get the error string, you could say.
<body>
${status} ${reason}
</body>
Define error.jsp with nice css for all kind of errors you would expect.
These are not the only ways. With spring you have freedom to do anything. I have seen few ppl rendering json object for error message.
To answer your another question of if the error happens in the service called by the controller is depend on your scenario. For example you are trying to read the user store, if the user store not available error happens, I would handle there itself to read from another replica user store if one available and If I found user does not exist I would leave the exception to the controller to throw.

Resources