J'étais confus au sujet de la validation (qui peut être définie par annotation) du côté javax et de la méthode de test de validation du côté Spring, je vais donc le résumer comme suit. Au fait, vous pensez peut-être que vous devriez tout faire avec des annotations personnalisées du côté javax, mais je n'ai pas pu le tester comme prévu, alors j'ai dû le faire du côté Spring.
Comment accéder à un champ dans votre propre validation lors de l'exécution d'un test
Commencez par vérifier la classe cible.
PasswordForm.java
package com.ssp_engine.user.domain.model;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import com.ssp_engine.user.domain.model.validation.ConfirmPassword;
import com.ssp_engine.user.domain.model.validation.ValidGroup1;
import com.ssp_engine.user.domain.model.validation.ValidGroup2;
import com.ssp_engine.user.domain.model.validation.ValidGroup3;
import com.ssp_engine.user.domain.model.validation.ValidGroup4;
import lombok.Data;
@Data
@ConfirmPassword(field = "password", groups = ValidGroup4.class)
public class PasswordForm {
@NotBlank(groups = ValidGroup1.class)
private String currentPassword;
@NotBlank(groups = ValidGroup1.class)
@Length(min = 4, max = 8, groups = ValidGroup2.class)
@Pattern(regexp="^[a-zA-Z0-9]+$", groups = ValidGroup3.class)
private String password;
@NotBlank(groups = ValidGroup1.class)
private String confirmPassword;
}
Il s'agit d'une classe de formulaire pour modifier votre propre mot de passe sur l'écran de gestion d'un service Web général comme celui-ci.
currentPassword
est le mot de passe actuellement connecté
password
est le mot de passe à changer
confirmPassword
est le mot de passe de confirmation
Ainsi, chacun a la validation des expressions régulières @ NotBlank
et @ Pattern
.
Il y a une validation dans currentPassword
pour comparer avec le mot de passe actuellement connecté, qui est comme suit.
LoginPassAndFormPassValidator.java
package com.ssp_engine.user.domain.model.validation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import com.ssp_engine.user.domain.model.PasswordForm;
@Component
public class LoginPassAndFormPassValidator implements Validator {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public boolean supports(Class<?> clazz) {
return PasswordForm.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
PasswordForm form = (PasswordForm) target;
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UserDetails principal = (UserDetails) auth.getPrincipal();
String userPass = principal.getPassword();
if (form.getCurrentPassword() == null) {
return;
}
if (!this.passwordEncoder.matches(form.getCurrentPassword(), userPass)) {
errors.rejectValue("currentPassword",
"LoginPassAndFormPassValidator.PasswordForm.currentPassword",
"Il est différent du mot de passe auquel vous êtes connecté.");
}
}
}
Côté contrôleur, je l'utilise après @ InitBinder
.
Nous testerons ces validateurs Spring et Javax.
Et comme pour le test, il est plus facile de séparer le contenu du test car il n'est plus long de séparer le test côté contrôleur et le test de la classe de formulaire, donc ça ressemble à ceci.
PasswordFormTests.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class PasswordFormTests {
private PasswordForm passwordForm = new PasswordForm();
private BindingResult bindingResult = new BindException(passwordForm, "PasswordForm"); //①
@Autowired
@Qualifier("loginPassAndFormPassValidator") //②
/*Côté ressort*/
private org.springframework.validation.Validator loginPassAndFormPassValidator;
/*côté javax*/
private static Validator validator; //③
@BeforeClass
processus d'initialisation public static void() { //④
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
@Before
public void Définir la valeur() throws Exception{ //⑤
this.passwordForm.setCurrentPassword("currentpassword");
this.passwordForm.setConfirmPassword("password");
this.passwordForm.setPassword("password");
}
}
① ・ ・ ・ Champ de réception du résultat après l'exécution du validateur
② ・ ・ ・ Puisqu'il était nécessaire de spécifier explicitement quelle classe, elle a été spécifiée avec @ Qualifier
.
③ ・ ④ ・ ・ ・ Je voulais obtenir un bean en le spécifiant avec @ AutoWired
, mais cela n'a pas fonctionné, donc je crée explicitement un bean.
⑤ ・ ・ ・ Une valeur est définie pour l'objet cible.
Quand je serai prêt, je vais faire un test gorigori, et ça ressemble à ça. Vérifiez d'abord qu'il n'y a pas d'erreurs.
PasswordFormTests.java
@Test
@WithMockUser(username = "username",
password ="$2a$10$p3/Malw3/KWyfOlPwWoUCulx4iDb2C/nmo6x8P2svXjfJQ5ETLhG2",
roles = "USER")
public void aucune erreur() throws Exception{
loginPassAndFormPassValidator.validate(this.passwordForm, bindingResult); //①
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup1.class,ValidGroup2.class,ValidGroup3.class,ValidGroup4.class); //②
assertThat(bindingResult.getFieldError(), is(nullValue())); //③
assertThat(violations.size(), is(0)); //④
}
① ・ ・ ・ La validation côté Spring est en cours d'exécution. L'objet cible est spécifié dans le premier argument et l'objet bindingResult
qui stocke le résultat est spécifié dans le deuxième argument.
② ・ ・ ・ ConstraintViolation
renvoie un ensemble d'objets qui stockent le contenu de la violation de contrainte, et l'objet cible est le premier argument de validate. Puisque ValidGroup a été spécifié dans le deuxième argument, la validation activée est spécifiée.
② ・ ・ ・ Reçoit le résultat du côté Spring avec bindingResult
et vérifie s'il est Null.
③ ・ ・ ・ Le résultat du côté Javax est dimensionné avec violations.size ()
et vérifié pour voir s'il est égal à 0.
@ WithMockUser
est dans l'état connecté car il était nécessaire d'obtenir les informations de connexion avec loginPassAndFormPassValidator
.
Quand j'ai découvert qu'il n'y avait pas d'erreur, je l'ai écrit comme ça.
PasswordFormTests.java
@Test
@WithMockUser(username = "username",
password ="currentpassword",
roles = "USER")
public void Le chemin de connexion et le chemin d'entrée sont différents() throws Exception{
loginPassAndFormPassValidator.validate(this.passwordForm, bindingResult);
assertThat(bindingResult.getFieldError("currentPassword"), is(bindingResult.getFieldError()));
assertThat(bindingResult.getFieldError().getDefaultMessage(), is("Il est différent du mot de passe auquel vous êtes connecté."));
}
@Test
public void Le mot de passe actuel est vide() throws Exception{
this.passwordForm.setCurrentPassword("");
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup1.class);
assertThat(violations.size(), is(1));
assertThat(getAnnotation(violations, "currentPassword"), is(instanceOf(NotBlank.class))); //①
}
private Annotation getAnnotation(Set<ConstraintViolation<PasswordForm>> violations, String path) { //②
return violations.stream()
.filter(cv -> cv.getPropertyPath().toString().equals(path))
.findFirst()
.map(cv -> cv.getConstraintDescriptor().getAnnotation())
.get();
}
① ・ ・ ・ Vérifiez quelle annotation est à l'origine de l'erreur. (2) ... Je crée une méthode pour obtenir une instance de l'annotation qui a été lue avec une erreur.
L'image entière ressemble à ceci.
PasswordFormTests.java
package com.ssp_engine.user.domain.model;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.lang.annotation.Annotation;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import com.ssp_engine.user.domain.model.validation.ConfirmPassword;
import com.ssp_engine.user.domain.model.validation.ValidGroup1;
import com.ssp_engine.user.domain.model.validation.ValidGroup2;
import com.ssp_engine.user.domain.model.validation.ValidGroup3;
import com.ssp_engine.user.domain.model.validation.ValidGroup4;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PasswordFormTests {
private PasswordForm passwordForm = new PasswordForm();
private BindingResult bindingResult = new BindException(passwordForm, "PasswordForm");
@Autowired
@Qualifier("loginPassAndFormPassValidator")
/*Côté ressort*/
private org.springframework.validation.Validator loginPassAndFormPassValidator;
/*côté javax*/
private static Validator validator;
@BeforeClass
processus d'initialisation public static void() {
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
@Before
public void Définir la valeur() throws Exception{
this.passwordForm.setCurrentPassword("currentpassword");
this.passwordForm.setConfirmPassword("password");
this.passwordForm.setPassword("password");
}
@Test
@WithMockUser(username = "username",
password ="$2a$10$p3/Malw3/KWyfOlPwWoUCulx4iDb2C/nmo6x8P2svXjfJQ5ETLhG2",
roles = "USER")
public void aucune erreur() throws Exception{
loginPassAndFormPassValidator.validate(this.passwordForm, bindingResult);
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup1.class,ValidGroup2.class,ValidGroup3.class,ValidGroup4.class);
assertThat(bindingResult.getFieldError(), is(nullValue()));
assertThat(violations.size(), is(0));
}
@Test
@WithMockUser(username = "username",
password ="currentpassword",
roles = "USER")
public void Le chemin de connexion et le chemin d'entrée sont différents() throws Exception{
loginPassAndFormPassValidator.validate(this.passwordForm, bindingResult);
assertThat(bindingResult.getFieldError("currentPassword"), is(bindingResult.getFieldError()));
assertThat(bindingResult.getFieldError().getDefaultMessage(), is("Il est différent du mot de passe auquel vous êtes connecté."));
}
@Test
public void Le mot de passe actuel est vide() throws Exception{
this.passwordForm.setCurrentPassword("");
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup1.class);
assertThat(violations.size(), is(1));
assertThat(getAnnotation(violations, "currentPassword"), is(instanceOf(NotBlank.class)));
}
@Test
Le mot de passe public void est vide() throws Exception{
this.passwordForm.setPassword("");
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup1.class);
assertThat(violations.size(), is(1));
assertThat(getAnnotation(violations, "password"), is(instanceOf(NotBlank.class)));
}
@Test
public void Le mot de passe de confirmation est vide() throws Exception{
this.passwordForm.setConfirmPassword("");
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup1.class);
assertThat(violations.size(), is(1));
assertThat(getAnnotation(violations, "confirmPassword"), is(instanceOf(NotBlank.class)));
}
@Test
public void Lorsque le mot de passe de confirmation et le mot de passe d'entrée sont différents() throws Exception{
this.passwordForm.setPassword("aiueo");
this.passwordForm.setConfirmPassword("kakikukeko");
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup4.class);
assertThat(violations.size(), is(1));
assertThat(getAnnotation(violations, "password"), is(instanceOf(ConfirmPassword.class)));
}
@Test
public void Lorsque le mot de passe contient 8 caractères ou plus() throws Exception{
this.passwordForm.setPassword("aiueokakikukeko");
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup2.class);
assertThat(violations.size(), is(1));
assertThat(getAnnotation(violations, "password"), is(instanceOf(Length.class)));
}
@Test
public void Lorsque le mot de passe est de 4 caractères ou moins() throws Exception{
this.passwordForm.setPassword("aiu");
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup2.class);
assertThat(violations.size(), is(1));
assertThat(getAnnotation(violations, "password"), is(instanceOf(Length.class)));
}
@Test
public void Lorsque le mot de passe est autre que des caractères alphanumériques demi-largeur() throws Exception{
this.passwordForm.setPassword("C'est un test");
Set<ConstraintViolation<PasswordForm>> violations =
validator.validate(this.passwordForm,ValidGroup3.class);
assertThat(violations.size(), is(1));
assertThat(getAnnotation(violations, "password"), is(instanceOf(Pattern.class)));
}
private Annotation getAnnotation(Set<ConstraintViolation<PasswordForm>> violations, String path) {
return violations.stream()
.filter(cv -> cv.getPropertyPath().toString().equals(path))
.findFirst()
.map(cv -> cv.getConstraintDescriptor().getAnnotation())
.get();
}
}
Je serais reconnaissant si quelqu'un pouvait le trouver utile.
Recommended Posts