writing junit for custom spring validator class - spring

How to write a unit testing case for custom spring validator implementation class . For example
public class RegistrationValidator implements Validator.
Just wanted to know the various approaches . Am new to spring so exploring all options.
Thanks,
S

Create an Errors object,
Create an instance of your Validator,
invoke yourValidator(testData, errors)
Check that Errors is modified in the way you expected, in dependence to testData
#Test
public void testValidateWithUserWithoutLogin() {
User u = new User(); //your domain object, for example a user with "login=null"
Errors errors = new BeanPropertyBindingResult(u, "u");
MyValidator validator = newValidator();
validator.validate(p, errors); // 'validator' under test
assertTrue(errors.hasErrors());
assertNotNull( errors.getFieldError("login") );
}
BTW: you should have a look at JSR 303-Bean Validation, it is also supported by Spring 3.0. But it is easyer to use (less code) for the most use cases.

Related

Spring Graphql - How to use a custom DataFetcherExceptionHandler and override the default one?

I'm new to spring graphql and I was trying to implement my own DataFetcherExceptionHandler so I can wrap all exceptions with my custom one.
I've implemented my custom class that implements DataFetcherExceptionHandler but it seems like it still uses the default one, the SimpleDataFetcherExceptionHandler.
How can I make my custom DataFetcherExceptionHandler the default one for the graphql exceptions?
My class:
#Slf4j
#AllArgsConstructor
#Component
public class GraphqlExceptionHandler implements DataFetcherExceptionHandler {
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
Throwable exception = handlerParameters.getException();
SourceLocation sourceLocation = handlerParameters.getSourceLocation();
ResultPath path = handlerParameters.getPath();
MyCustomException error = exposedException(exception, sourceLocation, path);
return DataFetcherExceptionHandlerResult.newResult().error(error).build();
}
#Override
public CompletableFuture<DataFetcherExceptionHandlerResult> handleException(DataFetcherExceptionHandlerParameters handlerParameters) {
return CompletableFuture.completedFuture(this.onException(handlerParameters));
}
Note: I'm not sure if I can use my custom exception like that, but I'm not able to test it while I can't make the exception handler the default one.
With Spring for GraphQL you can implement a DataFetcherExceptionResolver or more specifically a DataFetcherExceptionResolverAdapter that you can for example annotate with #Component to register it automatically.
The DataFetcherExceptionHandler from graphql-java is used by Spring for GraphQL internally to delegate to your DataFetcherExceptionResolver classes.
Inside your own DataFetcherExceptionResolverAdapter, you can get the informations that are available as DataFetcherExceptionHandlerParameters (Path, SourceLocation and so on) in a DataFetcherExceptionHandler from the DataFetchingEnvironment that is passed to DataFetcherExceptionResolverAdapter resolveToSingleError and resolveToMultipleErrors methods.
See here for more informations: https://docs.spring.io/spring-graphql/docs/current/reference/html/#execution-exceptions
You can find an example implementation here: https://github.com/nilshartmann/spring-graphql-training/blob/main/app/publy-backend/src/main/java/nh/publy/backend/graphql/runtime/PublyGraphQLExceptionResolver.java

spock, mock a method response in a spring bean

I have an integration test written in groovy (spock) in spring boot application. One of the application beans is called Validator it has the follwoing method:
public void validateIssueDates(final List<Timestamp> issueDates) {
issueDates.forEach(issueDate -> {
final Timestamp now = Timestamp.valueOf(LocalDateTime.now());
if (issueDate.before(now)) {
throw new IllegalArgumentException("Issue date is before current date");
}
});
}
In the Validator class there are other methods. In my spock integration test I would like to mock response for that particular method only. In the following way:
Validator.validateIssueDates(_) >> null
I want other validations to take place, but not this one. Bascially I want to achieve this but with spock. I would like to eliminate the validateIssueDates() method from being executed
solution using Spock
It's done using [#SpringSpy][2].
First we annotate field with a spring bean we want to wrap in spy object. For example:
#SpringSpy
private CarValidator carValidator;
then in our test, in then part we define how we want to override method from a a bean/spy:
then:
3 * carValidator.validateIssueDates(_) >> null
Solution using Mockito (as an additional approach, it's not related to spock solution)
I have got that pretty easy using spy in Mockito. Despite many trials (and errors) with spock's spy, It just doesn't want to work. If I get that, I post it here. For now, I can only share Mockito solution:
#Profile("test")
#Configuration
public class BeanConfig {
#Bean
#Primary
public CarValidator getCarValidatorSpy(CarValidator validator) {
CarValidator carValidatorSpy = Mockito.spy(validator);
Mockito.doNothing().when(carValidatorSpy).validateIssueDates(Mockito.any(CarDto.class));
return carValidatorSpy;
}
}
That's all. Seems fairly straightforward.

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.

Configuring Spring MockMvc to use custom argument resolver before built-in ones

I have a straightforward test case. I have a controller which has a parameter of a type Spring doesn't support by default, so I wrote a custom resolver.
I create the mock mvc instance I'm using like so:
mvc = MockMvcBuilders.standaloneSetup(controller).setCustomArgumentResolvers(new GoogleOAuthUserResolver()).build();
However, Spring is also registering almost 30 other argument resolvers, one of which is general enough that it is getting used to resolve the argument before mine. How can I set or sort the resolvers so that mine is invoked first?
This worked for me without reflection:
#RequiredArgsConstructor
#Configuration
public class CustomerNumberArgumentResolverRegistration {
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#PostConstruct
public void prioritizeCustomArgumentResolver () {
final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(Objects.requireNonNull(requestMappingHandlerAdapter.getArgumentResolvers()));
argumentResolvers.add(0, new CustomerNumberArgumentResolver());
requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers);
}
}
The issue was that the People class the Google OAuth library I am using extends Map and the mock servlet API provides no way to manipulate the order in which the handlers are registered.
I ended up using reflection to reach into the mocks guts and remove the offending handler.

Why is my Spring 3 Validator Validating Everything on the Model?

I have a spring 3 controller with a validator for one of the methods. It insists on validating every object on the model. Would anyone be able to explain to me why it does this or if I'm doing something wrong?
According to the docs, 5.7.4.3 Configuring a JSR-303 Validator for use by Spring MVC (http://static.springsource.org/spring/docs/3.0.0.RC3/spring-framework-reference/html/ch05s07.html)
With JSR-303, a single javax.validation.Validator instance typically validates all model objects that declare validation constraints. To configure a JSR-303-backed Validator with Spring MVC, simply add a JSR-303 Provider, such as Hibernate Validator, to your classpath. Spring MVC will detect it and automatically enable JSR-303 support across all Controllers.
Example:
#Controller
public class WhaleController {
#Autowired
private Validator myValidator;
#Autowired
private WhaleService whaleService;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(this.myValidator);
}
#RequestMapping(value="/save-the-whales")
#Transactional
public void saveTheWhales(#Valid WhaleFormData formData, BindingResult errors, Model model) {
if (!errors.hasFieldErrors()) {
Whale whale = new Whale();
whale.setBreed( formData.getBreed() );
this.whaleService.saveWhale( whale );
model.addAttribute("whale", whale);
}
model.addAttribute("errors", errors.getFieldErrors());
}
}
When run it will complain that Whale is an invalid target for myValidator (which is set to validate WhaleFormData, and does so fine). Whale is a POJO with no validation constraints, annotation and no config anywhere. Through trial and error I've found that ANY object placed on the model will attempt to be validated and fail if the validator is not setup to handle it. Primitives are just fine.
Can anyone tell me why this is, point me to the appropriate documentation and/or tell me the best way to put something on the model without having it validated?
In the case above I would like to place "whale" on the model as it will now have a unique whaleId() that it received from my persistence layer.
Thanks!
I guess this behaviour is not covered in the documentation well.
The problem is caused by the following:
By default, #InitBinder-annotated method is called for each non-primitive model attribute, both incoming and outcoming (the purpose of calling it for outcoming attibutes is to allow you to register custom PropertyEditors, which are used by form tags when rendering a form).
DataBinder.setValidator() contains a defensive check that call Validator.supports() and throws an exception if false is returned. So, there is no attempt to perform a validation, just an early check.
The solution is to restrict the scope of #InitBinder to particular attribute:
#InitBinder("whaleFormData")
protected void initBinder(WebDataBinder binder) { ... }

Resources