[JAVA] Beschleunigen Sie das Testen von Validatoren, für die DI im Spring Boot erforderlich ist

Bei der Suche nach einer schnelleren Ausführungszeit für Komponententests im Spring Boot-Projekt habe ich herausgefunden, wie die Testausführung von benutzerdefinierten Validatoren, für die DI erforderlich ist, beschleunigt werden kann.

Bestätigungsumgebung

Hintergrund

Manchmal möchte ich eine DB-Verbindung mit Bean Validation herstellen, z. B. beim Überprüfen der Eindeutigkeit eines Eingabewerts, und ich habe mit DI eine Reihe von benutzerdefinierten Validatoren wie folgt erstellt.

@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;
        }
        ...

Wenn Sie jedoch einen Komponententest für einen solchen benutzerdefinierten Validator schreiben, müssen Sie den Validator mit den abhängigen Komponenten DI initialisieren. Ich habe also Tests mit @ SpringBootTest geschrieben, aber die Spring-Initialisierung dauert lange und ich kann die Zunahme der Ausführungszeit nicht ertragen, wenn die Anzahl der benutzerdefinierten Validatoren zunimmt.

@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());
    }

    ...
}

Lösung

Durch Angabe des Attributs classes der Annotation @ SpringBootTest können Sie nur die erforderlichen Komponenten initialisieren und die Initialisierungszeit beim Teststart verkürzen.

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

    @Autowired
    private Validator validator;

    @MockBean
    private EmployeeDao employeeDao;

    ...
}

Verbesserungseffekt

Nur als Referenz wurde es auf validatorbezogene Tests mit weniger als 30 Klassen und weniger als 500 Methoden angewendet, und die Ausführungszeit wurde auf weniger als die Hälfte reduziert: +1:

Ergänzung

Die Testausführungszeit ist schneller, aber es ist ein Problem, dass Sie die Klassen jedes Mal in DI auflisten müssen. Während benutzerdefinierte Validator-Testklassen nur wenige DI-Komponenten enthalten, ist dies ziemlich gut, aber für Klassen mit vielen Abhängigkeiten ist es möglicherweise besser, die Wartbarkeit der Ausführungsgeschwindigkeit vorzuziehen.

Wenn Sie ein Team entwickeln, ist es außerdem schwierig, solche Implementierungsregeln zu vereinheitlichen, und es ist einfach, die Anzahl der Testcodes zu erhöhen, die ohne Ihr Wissen nur "@ SpringBootTest" angeben. Es scheint gut, ArchUnit usw. zu verwenden, um einen Mechanismus einzuführen, der mechanisch bemerkt.

@Test
void validatorTestShouldeRestrictAutowiredComponent() {
    classes().that().haveNameMatching(".+ValidatorTest$").and().areAnnotatedWith(SpringBootTest.class)
        .should()
        .beAnnotatedWith(new DescribedPredicate<>("@SpringBootTest(classes = {ValidationAutoConfiguration.class, ...}Begrenzen Sie die Komponenten auf DI mit") {
            @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);
}

Zusammenfassung

In diesem Artikel habe ich Ihnen gezeigt, wie Sie den Komponententest von benutzerdefinierten Validatoren in einem Projekt mithilfe von Spring Boot beschleunigen können. Ich habe lange gewusst, dass die Testausführung von benutzerdefinierten Validatoren langsam ist, aber ich konnte diese Methode nicht leicht finden, indem ich im Internet suchte. Ich hoffe, dass sie für diejenigen hilfreich ist, die dieselben Probleme haben.

Recommended Posts

Beschleunigen Sie das Testen von Validatoren, für die DI im Spring Boot erforderlich ist
DI SessionScope Bean im Spring Boot 2-Filter
Über DI des Frühlings ①
Erster Frühlingsstiefel (DI)
Über DI des Frühlings ②
Geben Sie die statische Ressourcencodierung in Spring Boot an
Untersuchte asynchrone Ausführung von Abfragen in Spring Boot 1.5.9
Verwendung von CommandLineRunner im Spring Batch von Spring Boot
Geben Sie spring.profiles.active über context-param in web.xml in Spring Boot an
Legen Sie den Kontextparameter in Spring Boot fest
Spring Boot 2 Multiprojekt mit Gradle
Einführung in Spring Boot ~ ~ DI ~
Beispiel einer Webanwendung, die mit Spring Boot 1.5 mehrere Datenbanken verwaltet
Wichtige Änderungen in Spring Boot 1.5
NoHttpResponseException in Spring Boot + WireMock
Einführung der Bibliothek ff4j, die Feature Toggle mit Spring Boot realisiert
Beispielcode, der die Moustache-Vorlagen-Engine mit Spring Boot verwendet
Rufen Sie in Spring Boot eine Proxy-Instanz der Komponente selbst ab
Formular, das den Wert des sich wiederholenden Elements in Spring MVC erhält
Unbekannter Fehler in Zeile 1 von pom.xml bei Verwendung von Spring Boot in Eclipse
Frühlingsstiefel Hallo Welt in Eclipse
Spring Boot-Anwendungsentwicklung in Eclipse
Memorandum beim Spring Boot 1.5.10 → Spring Boot 2.0.0
Schreiben Sie den Testcode mit Spring Boot
Nachricht erlöschen (Spring Boot)
Implementieren Sie die REST-API mit Spring Boot
Was ist @Autowired im Spring Boot?
Implementieren Sie die Spring Boot-Anwendung in Gradle
Ein Memo, das Spring Boot berührte
Verwendung von Thymeleaf mit Spring Boot
[Spring Boot] Liste der Validierungsregeln, die in der Eigenschaftendatei für Fehlermeldungen verwendet werden können
Eine Geschichte über ein in Java geschriebenes Spring Boot-Projekt, das Kotlin unterstützt
Lösen Sie Nullzeiger für verschiedene Sitzungen während des MVC-Tests von Spring Boot auf.
Autowired-Felder in einer Klasse, die TextWebSocketHandler in Spring Boot erbt, werden NULL
Persönliches Memo Merkmale von Spring Boot (hauptsächlich aus DI-Sicht)
Mein Memorandum, dass ich ValidationMessages.properties UTF8 in Spring Boot erstellen möchte
Starten Sie mit IntelliJ ein (altes) Spring Boot-Projekt
Erstellen Sie mit Gradle ein Spring Boot + Docker-Image
Statische Dateizugriffspriorität beim Spring Boot
Spring Boot-Protokoll im JSON-Format ausgeben
Memorandum zum Herunterladen lokaler Dateien mit Spring Boot
Erstellen Sie mit IntelliJ ein Java Spring Boot-Projekt
Lösen Sie die Thymeleaf-Syntaxprüfung in Spring Boot
[Trainieren! ] Zeigen Sie Hello World mit Spring Boot an
WebMvcConfigurer-Memorandum von Spring Boot 2.0 (Spring 5)
Verwenden Sie die DynamoDB-Abfragemethode mit Spring Boot
Ändern Sie das Sitzungszeitlimit in Spring Boot
Einführung des Spring Boot Actuator, einer Funktion, die die Bedienung von Spring Boot-Anwendungen erleichtert
Organisieren Sie die Unterschiede im Verhalten von @NotBlank, @NotEmpty und @NotNull mit Spring Boot + Thymeleaf
Rufen Sie den in der Controller-Klasse von Spring Boot definierten Pfad als Liste ab
So legen Sie Umgebungsvariablen in der Eigenschaftendatei der Spring-Boot-Anwendung fest