Using Spring's MockMvc framework, how do I test the value of an attribute of an attribute of my model? - spring

I’m using Spring 3.2.11.RELEASE and JUnit 4.11. I’m using Spring’s org.springframework.test.web.servlet.MockMvc framework to test a controller method. In one test, I have a model that is populated with the following object:
public class MyObjectForm
{
private List<MyObject> myobjects;
public List<MyObject> getMyObjects() {
return myobjects;
}
public void setMyObjects(List<MyObject> myobjects) {
this.myobjects = myobjects;
}
}
The “MyObject” object in turn has the following field …
public class MyObject
{
…
private Boolean myProperty;
Using the MockMvc framework, how do I check that the first item in the “myobjects” list has an attribute “myProperty” equal to true? So far I know it goes something like this …
mockMvc.perform(get(“/my-path/get-page”)
.param(“param1”, ids))
.andExpect(status().isOk())
.andExpect(model().attribute("MyObjectForm", hasProperty("myobjects[0].myProperty”, Matchers.equalTo(true))))
.andExpect(view().name("assessment/upload"));
but I’m clueless as to how to test the value of an attribute of an attribute?

You can nest hasItem and hasProperty matchers if your object has a getter getMyProperty.
.andExpect(model().attribute("MyObjectForm",
hasProperty("myObjects",
hasItem(hasProperty("myProperty”, Matchers.equalTo(true))))))
If you know how much objects are in the list than you can check the first item with
.andExpect(model().attribute("MyObjectForm",
hasProperty("myObjects", contains(
hasProperty("myProperty”, Matchers.equalTo(true)),
any(MyObject.class),
...
any(MyObject.class)))));

In case anyone else comes across this problem. I ran into a similar issue trying to test a value of an attribute (firstName), of a class (Customer) in a List< Customer >. Here is what worked for me:
.andExpect(model().attribute("customerList", Matchers.hasItemInArray(Matchers.<Customer> hasProperty("firstName", Matchers.equalToIgnoringCase("Jean-Luc")))))

Related

Spring 5 Webflux functional endpoints - How to perform input validation?

According to the current doc (5.0.0.RELEASE) Spring Webflux supports validation when working with annotated controllers:
By default if Bean Validation is present on the classpath — e.g.
Hibernate Validator, the LocalValidatorFactoryBean is registered as a
global Validator for use with #Valid and Validated on #Controller
method arguments.
However nothing is said about how to automate it with functional endpoints. In fact, the only example of input processing in the documentation doesn't validate anything:
public Mono<ServerResponse> createPerson(ServerRequest request) {
Mono<Person> person = request.bodyToMono(Person.class);
return ServerResponse.ok().build(repository.savePerson(person));
}
Are we supposed to do this manually or there is some automatic way to do it?
In Spring version 5.0, there is no automatic way to do validation in functional endpoints, and as such validation must be done manually.
Though there are currently no concrete plans to do so, we might add some sort of validation in the future. But even then it will be an explicit method call, and not an automatic mechanism. Overall, the functional endpoint model is designed to be a lot more explicit than the annotation-based model.
As arjen-poutsma said, it seems there is no way of running automated validations on Spring 5 functional endpoints.
Spring documentation is not very clear about this, and it doesn't suggest any approach.
On this Baeldung article, you'll find an idea on how you can run validations using this approach (disclaimer: I'm the writer of the article :) )
In a nutshell, you can follow these steps:
Implement Spring Validators to evaluate your resources
Create an abstract class with the basic procedure that any handler will follow when processing a request, leaving up to the children classes what to do when the data is valid
Make your request handler classes extend this abstract class, implementing this abstract method, stating the body it will be expecting, and what validator needs to be used to validate it
EDIT:
I've been following this related Spring issue, and it seems we now count with official documentation regarding this subject: https://github.com/spring-projects/spring-framework/blob/master/src/docs/asciidoc/web/webflux-functional.adoc#validation
The suggested approach is to use validators as explained in the article.
At the current version(2.0.4.RELEASE) there isn't a way to do automatic validation with handles, however you always could make a manual validation like this:
#Slf4j
#Component
#FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
#RequiredArgsConstructor
public class MyHandlerValidator implements HandlerValidator<MyResource> {
Validator validator;
#Override
public void callValidator(final MyResource fdr) {
final DataBinder binder = new DataBinder(fdr);
binder.setValidator(validator);
binder.validate();
if (binder.getBindingResult().hasErrors()) {
final String reason = binder.getBindingResult().getFieldError().toString();
log.error(reason);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, reason);
}
}
}
The thing with this, its that the you should throw a WebExchangeBindException like automatic validation does, however i could't create a MethodParameter witch is a dependency to create this exception.
UPDATE:
Spring show us a way to do it, which is similar to my solution, but, not enough in my opinion on documentation
Just to demo some working code. If you need simple validation based on the object annotations like:
#Value
#Builder
#Jacksonized
public class SigninRequest {
#NotBlank(message = "The username is mandatory")
#Email(message = "The username should be valid Email")
String username;
#NotBlank(message = "The password is mandatory")
String password;
}
At the handler you need just one simple additional operator doOnNext:
#Component
#RequiredArgsConstructor
public class AuthHandler {
private final AuthService authService;
private final ObjectValidator validator;
public Mono<ServerResponse> signin(ServerRequest request) {
return ok().body(
request.bodyToMono(SigninRequest.class)
.doOnNext(validator::validate) //<-- just one single line
.flatMap(login -> authService.authenticate(login.getUsername(), login.getPassword())),
AuthResult.class);
}
}
The ObjectValidator is doing actual validation and throws the runtime exception with the 4xx error in case of validation errors:
#Component
#RequiredArgsConstructor
public class ObjectValidator {
private final Validator validator;
public <T> T validate(T object) {
var errors = validator.validate(object);
if (errors.isEmpty()) {
return object;
} else {
String errorDetails = errors.stream().map(er -> er.getMessage()).collect(Collectors.joining(", "));
throw new ObjectValidationException(errorDetails);
}
}
}
And the exception:
#ResponseStatus(code = HttpStatus.UNPROCESSABLE_ENTITY)
public class ObjectValidationException extends RuntimeException {
public ObjectValidationException(String errorDetails) {
super("Please supply the valid data: " + errorDetails);
}
}
If you properly setup global error handling you can keep you handler code clean and reuse the object validator across all your handlers.

Can I "inject" values from message resources into model objects before implicit Jackson serialisation?

I have a REST API built with Spring Boot / Spring MVC, using the implicit JSON serialization via Jackson.
Now, just before the implicit serialization, I would like to "inject" some UI texts from message resources into the objects that Jackson converts into JSON. Is there some neat, simple way to do this?
As a much simplified example, below I'd like to set Section title to a user-visible value, based purely based on its SectionType.
(Sure, I could hardcode the UI texts in SectionType, but I'd rather keep them separate, in resource files, because it's cleaner, and they might be localised at some point. And I can't autowire MessageSource in the entities / model objects which are not Spring-managed.)
#Entity
public class Entry {
// persistent fields omitted
#JsonProperty
public List<Sections> getSections() {
// Sections created on-the-fly, based on persistent data
}
}
public class Section {
public SectionType type;
public String title; // user-readable text whose value only depends on type
}
public enum SectionType {
MAIN,
FOO,
BAR;
public String getUiTextKey() {
return String.format("section.%s", name());
}
}
Somewhere in a #RestController:
#RequestMapping(value = "/entry/{id}", method = RequestMethod.GET)
public Entry entry(#PathVariable("id") Long id) {
return service.findEntry(id);
}
UI texts that I'd like to keep separate from code (messages_en.properties):
section.MAIN=Main Section
section.FOO=Proper UI text for the FOO section
section.BAR=This might get localised one day, you know
And what I'd like to do in a Spring-managed service/bean somewhere (using Messages, a very simple helper wrapping a MessageSource):
section.title = messages.get(section.type.getUiTextKey())
Note that if I call entry.getSections() and set the title for each, it will not affect the JSON output, since the Sections are generated on the fly in getSections().
Do I have to go all the way to custom deseriazation, or is there a simpler way to hook into the model objects just before they get serialized by Jackson?
Sorry if the question is unclear; I can try to clarify if needed.
As I said in the comment you can write an Aspect around every controller method that returns Section.
I wrote a simple example. You have to modify it with the message source.
Controller:
#RestController
#RequestMapping("/home")
public class HomeController {
#RequestMapping("/index")
public Person index(){
Person person = new Person();
person.setName("evgeni");
return person;
}
}
Aspect
#Aspect
#Component
public class MyAspect {
#Around("execution(public Person com.example..*Controller.*(..))")//you can play with the pointcut here
public Object addSectionMessage(ProceedingJoinPoint pjp) throws Throwable {
Object retVal = pjp.proceed();
Person p = (Person) retVal; // here cast to your class(Section) instead of Person
p.setAge(26);//modify the object as you wish and return it
return p;
}
}
Since the aspect is also a #Component you can #Autowire in it.

Error while using parameter in #Scheduled in a spring 4.1 application

I have Spring 4.1 Application. I am trying to schedule based on value from property file. I have read this post. But I do not want the following way of EL inside #Scheduled
#Scheduled(fixedDelayString = "${my.fixed.delay.prop}")
public void readLog() {
...
}
Here is my class.
public class MyService {
#Value("${timerInMilliSeconds: 60000}")
private long timerinMilliSeconds;
public myService(){
}
#Scheduled(fixedRate = timerinMilliSeconds)
public void myTimer() {
//do stuff
}
}
I get this error.
The value for annotation attribute Scheduled.fixedRate must be a constant
expression
You can't do that; it's a limitation of the way annotations work - Strings in annotations have to be constants (they are stored in the class and can't be different for each instance).
By the way ${my.fixed.delay.prop} is not "EL", it's a property placeholder.

Can I use both #Post and #Get on the same method

I would like to use both #Post and #Get on the same method like
#GET
#POST
#Path("{mode}")
public void paymentFinish(#PathParam("mode") String mode, String s) {
logger.debug("Enter PayStatus POST");
logger.debug(mode);
}
Even I write like this, I got error. What I want is whatever get or post to the sameurl, the same method works. Is it possible? Now I separate two methods, one for get and one for post.
Unfortunately, only one should be used in order to avoid Jersey exception.
But you could do something like :
#GET
#Path("{mode}")
public void paymentFinish(#PathParam("mode") String mode, String s) {
commonFunction(mode);
}
#POST
#Path("{mode}")
public void paymentFinishPOST(#PathParam("mode") String mode, String s) {
commonFunction(mode);
}
private void commonFunction(String mode)
{
logger.debug("Enter PayStatus POST");
logger.debug(mode);
}
By doing so, if you want to change inner behavior of your functions, you will only have to change one function.
Note that method name in java for get vs post need to be different.
After searching a lot trying to avoid the solution above, I found nothing....
Then I decided to create a custom annotation so I didn't have to waste time duplicating methods.
Here's the github link: Jersey-Gest
It allows you to create GET and Post Methods on a single Annotation by generating a new class from it.
I hope it helps you the same way it helped me :)
Edit:
If for some reason the above link stops working, here's what I did:
Created a compile-time annotation #RestMethod for class methods.
Created a compile-time annotation #RestClass for classes.
Create an AnnotationProcessor which generates a new class with Jersey's corresponding annotations and for each method creates a GET and a POST method which callsback to the original method annotated with #RestClass.
All methods annotated with #RestMethod must be static and contained within a class annotated with #RestClass.
Example (TestService.java):
#RestClass(path = "/wsdl")
public class TestService
{
#RestMethod(path = "/helloGest")
public static String helloGest()
{
return "Hello Gest!";
}
}
Generates something like (TestServiceImpl.java):
#Path("/wsdl")
#Produces("application/xml")
public class TestServiceImpl
{
#GET
#Path("/helloGest")
#Produces(MediaType.APPLICATION_XML)
public String helloGestGet()
{
return TestService.helloGest();
}
#POST
#Path("/helloGest")
#Consumes(MediaType.WILDCARD)
#Produces(MediaType.APPLICATION_XML)
public String helloGestPost()
{
return TestService.helloGest();
}
}

Why doesn't Mockito's when() get triggered?

I need to test a service class, but when I try to mock the dao class, it doesn't get triggered, thus not able to use ThenReturn().
I think that the problem is because I use an interface for my Dao and #Autowired in the service class (Spring MVC 3.1):
The interface:
public interface TestDao {
int createObject(Test test) throws NamingException;
}
The implementation:
#Repository
public class TestDaoImpl implements TestDao {
#Override
public int createObject(Test test) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new InsertNewTest(test), keyHolder);
return ((java.math.BigDecimal)keyHolder.getKey()).intValue();
}
}
The service:
public class RegTest {
#Autowired
TestDao testDao;
public int regTest(int .....) {
.
.
int cabotageId = testDao.createObject(test);
}
}
In the test I have:
#RunWith(MockitoJUnitRunner.class)
public class TestRegService {
#InjectMocks
private RegTest regTest = new RegTest();
#Mock
TestDao testDao;
#Test()
public void test() {
.
when(testDao.createObject(null)).thenReturn(100);
.
}
testDao.createObject(null) returns 0 (due to being mock'ed) and not 100 as I is trying to achieve.
Can anybody help, please?
Problem solved!
It was the passing test-object to createObject() that did not match. Using
testDao.createObject(any(Test.class))
did the trick!
If your test is actually passing a value to createObject, then when(testDao.createObject(null)... never gets matched. Rather than matching on null, you could match any instance of Test with testDao.createObject(any(Test.class))...
Also when you tried later to supply new Test() as the argument to match, it will literally try to match on that exact instance of Test, but presumably your real code is new-ing up a different one. So the use of Matchers.any(Test.class) as the parameter to match is the way to go.
Mockito injection mechanism don't know about Spring #Autowired or CDI #Inject annotations. It just tries to find the best candidate given the type and the name of the mock, and it can lookup private fields too. See the javadoc of #InjectMocks : http://docs.mockito.googlecode.com/hg/1.9.0/org/mockito/InjectMocks.html
The semantic you are using is correct, though if you are experiencing issues, I would rather look for incorrect interactions or incorrect arguments.
Are you sure the test variable in regTest.regTest(int...) is really null when passed to testDao.createObject(test) ?
I don't know if this is a typo in the example, but you have RegTest.regTest() calling createTest() rather than createObject(). Otherwise, I don't think #Autowired has anything to do with it, since your test itself is not running in a container with Spring management. If it is not a typo, and createTest is in fact a real and different method from createObject, then the default behaviour of a mocked object in Mockito is to return the appropriately-typed zero for numeric return types.
I think that you're right about the autowire not getting called. You could inject the dao yourself using the setTestDao() call instead. Mockito also supports spy which allows you to trace the objects code and just replace functions instead.

Resources