[JAVA] Accélérez les tests des validateurs qui nécessitent DI dans Spring Boot

Tout en explorant des temps d'exécution plus rapides des tests unitaires dans le projet Spring Boot, j'ai découvert comment accélérer l'exécution des tests de validateurs personnalisés qui nécessitent une DI.

Environnement de confirmation

Contexte

Parfois, je souhaite établir une connexion DB avec Bean Validation, par exemple lors de la vérification de l'unicité d'une valeur d'entrée, et j'ai créé un certain nombre de validateurs personnalisés à l'aide de DI comme suit.

@Documented
@Constraint(validatedBy = { EmployeeCodeUniqueValidator.class })
@Target({ TYPE })
@Retention(RUNTIME)
public @interface EmployeeCodeUniqueConstraint {
    ...
}
public class EmployeeCodeUniqueValidator implements ConstraintValidator<EmployeeCodeUniqueConstraint, Object> {
    private static final String EMPLOYEE_ID = "id";
    private static final String EMPLOYEE_CODE = "employeeCode";

    private final EmployeeDao employeeDao;

    public EmployeeCodeUniqueValidator(final EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

    @Override
    public boolean isValid(final Object value, final ConstraintValidatorContext context) {
        String employeeCode = getField(value, EMPLOYEE_CODE);
        if (employeeCode == null) {
            return true;
        }
        Optional<Employee> employeeOpt = employeeDao.findByEmployeeCode(employeeCode);
        if (employeeOpt.isEmpty()) {
            return true;
        }
        ...

Cependant, lors de l'écriture d'un test unitaire pour un tel validateur personnalisé, vous devez initialiser le validateur avec les composants dépendants DI. En conséquence, j'ai écrit des tests en utilisant @ SpringBootTest, mais l'initialisation Spring prend beaucoup de temps et je ne supporte pas l'augmentation du temps d'exécution à mesure que le nombre de validateurs personnalisés augmente.

@SpringBootTest
class EmployeeCodeUniqueValidatorTest {

    @Autowired
    private Validator validator;

    @MockBean
    private EmployeeDao employeeDao;

    @EmployeeCodeUniqueConstraint
    class Form {
        public String id;
        public String employeeCode;
    }

    @Test
    void newEmployeeCode() {
        when(employeeDao.findByEmployeeCode("012345")).thenReturn(Optional.empty());
        Form form = new Form();
        form.employeeCode = "012345";
        assertTrue(validator.validate(form).isEmpty());
    }

    ...
}

Solution

En spécifiant l'attribut classes de l'annotation @ SpringBootTest, vous pouvez initialiser uniquement les composants nécessaires et réduire le temps d'initialisation au démarrage du test.

- @SpringBootTest
+ @SpringBootTest(classes = {ValidationAutoConfiguration.class, EmployeeDao.class})
class EmployeeCodeUniqueValidatorTest {

    @Autowired
    private Validator validator;

    @MockBean
    private EmployeeDao employeeDao;

    ...
}

Effet d'amélioration

Pour référence seulement, il a été appliqué aux tests liés au validateur avec moins de 30 classes et moins de 500 méthodes, et le temps d'exécution a été réduit à moins de la moitié: +1:

Supplément

Le temps d'exécution du test sera plus rapide, mais c'est un problème que vous devez à chaque fois énumérer les classes à DI. La classe de test du validateur personnalisé a peu de composants DI, ce qui est plutôt bien, mais dans une classe avec de nombreuses dépendances, il peut être préférable de donner la priorité à la maintenabilité par rapport à la vitesse d'exécution.

De plus, si vous développez une équipe, il est difficile d'unifier de telles règles d'implémentation, et il est facile d'augmenter le nombre de codes de test qui spécifient uniquement @ SpringBootTest à votre insu. Il semble bon d'utiliser ArchUnit etc. pour introduire un mécanisme de notification mécanique.

@Test
void validatorTestShouldeRestrictAutowiredComponent() {
    classes().that().haveNameMatching(".+ValidatorTest$").and().areAnnotatedWith(SpringBootTest.class)
        .should()
        .beAnnotatedWith(new DescribedPredicate<>("@SpringBootTest(classes = {ValidationAutoConfiguration.class, ...}Limiter les composants à DI avec") {
            @Override
            public boolean apply(JavaAnnotation annot) {
                if (!annot.getRawType().getSimpleName().equals("SpringBootTest")) {
                    return true;
                }
                JavaClass[] classes = (JavaClass[]) annot.get("classes").or(new JavaClass[]{});
                return Arrays.stream(classes)
                    .anyMatch(clazz -> clazz.getSimpleName().equals("ValidationAutoConfiguration"));
            }
        })
        .check(ALL_CLASSES);
}

Résumé

Dans cet article, je vous ai montré comment accélérer les tests unitaires des validateurs personnalisés dans un projet à l'aide de Spring Boot. Je sais depuis longtemps que l'exécution des tests des validateurs personnalisés est lente, mais je n'ai pas pu trouver cette méthode facilement en recherchant sur le net, donc j'espère qu'elle sera utile pour ceux qui ont les mêmes problèmes.

Recommended Posts

Accélérez les tests des validateurs qui nécessitent DI dans Spring Boot
DI SessionScope Bean dans le filtre Spring Boot 2
À propos de DI of Spring ①
Première botte à ressort (DI)
À propos de DI of Spring ②
Spécifiez le codage des ressources statiques dans Spring Boot
Exécution asynchrone des requêtes examinée dans Spring Boot 1.5.9
Comment utiliser CommandLineRunner dans Spring Batch of Spring Boot
Spécifiez spring.profiles.active via context-param dans web.xml dans Spring Boot
Définir le paramètre contextuel dans Spring Boot
Multi-projets Spring Boot 2 avec Gradle
Introduction à Spring Boot ① ~ DI ~
Exemple d'application Web qui gère plusieurs bases de données avec Spring Boot 1.5
Changements majeurs dans Spring Boot 1.5
NoHttpResponseException dans Spring Boot + WireMock
Introduction de la bibliothèque ff4j qui réalise le basculement de fonctionnalités avec Spring Boot
Exemple de code qui utilise le moteur de modèle Moustache avec Spring Boot
Obtenez une instance proxy du composant lui-même dans Spring Boot
Formulaire qui reçoit la valeur de l'élément répétitif dans Spring MVC
Erreur inconnue dans la ligne 1 de pom.xml lors de l'utilisation de Spring Boot dans Eclipse
Spring Boot Hello World dans Eclipse
Développement d'applications Spring Boot dans Eclipse
Mémorandum lorsque Spring Boot 1.5.10 → Spring Boot 2.0.0
Écrire du code de test avec Spring Boot
Sortie de message (Spring boot)
Implémenter l'API REST avec Spring Boot
Qu'est-ce que @Autowired dans Spring Boot?
Implémenter l'application Spring Boot dans Gradle
Un mémo qui a touché Spring Boot
Comment utiliser Thymeleaf avec Spring Boot
[Spring Boot] Liste des règles de validation qui peuvent être utilisées dans le fichier de propriétés pour les messages d'erreur
Une histoire sur un projet Spring Boot écrit en Java qui prend en charge Kotlin
Résolvez les pointeurs nuls pour diverses sessions lors des tests MVC de Spring Boot.
Les champs auto-câblés dans une classe qui hérite de TextWebSocketHandler dans Spring Boot deviennent NULL
Mémo personnel Caractéristiques de Spring Boot (principalement du point de vue DI)
Mon mémorandum que je veux faire ValidationMessages.properties UTF8 dans Spring Boot
Lancer un (ancien) projet Spring Boot avec IntelliJ
Créer une image Spring Boot + Docker avec Gradle
Priorité d'accès aux fichiers statiques dans Spring Boot
Sortie du journal Spring Boot au format json
Mémorandum de téléchargement de fichier local avec Spring Boot
Créer un projet Java Spring Boot avec IntelliJ
Desserrer la vérification de la syntaxe de Thymeleaf dans Spring Boot
[Entraine toi! ] Affichez Hello World avec Spring Boot
Mémorandum WebMvcConfigurer de Spring Boot 2.0 (printemps 5)
Utiliser la méthode de requête DynamoDB avec Spring Boot
Modifier le délai d'expiration de la session dans Spring Boot
Présentation de Spring Boot Actuator, une fonctionnalité qui facilite l'utilisation des applications Spring Boot
Organisez les différences de comportement de @NotBlank, @NotEmpty et @NotNull avec Spring Boot + Thymeleaf
Obtenez le chemin défini dans la classe Controller de Spring Boot sous forme de liste
Comment définir des variables d'environnement dans le fichier de propriétés de l'application Spring Boot