While searching for faster unit test execution time in the Spring Boot project, I found out how to speed up the test execution of custom validators that require DI, so I will introduce it.
There are times when you want to make a DB connection with Bean Validation, such as when checking the uniqueness of an input value, and I created a number of custom validators using DI as follows.
@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;
}
...
However, when writing a unit test for such a custom validator, you need to initialize the validator with the dependent components DI. So far, I've written tests using @ SpringBootTest
, but Spring initialization takes a long time, and I can't stand the increase in execution time as the number of custom validators increases.
@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());
}
...
}
By specifying the classes
attribute of the @SpringBootTest
annotation, you can initialize only the required components and reduce the initialization time at test startup.
- @SpringBootTest
+ @SpringBootTest(classes = {ValidationAutoConfiguration.class, EmployeeDao.class})
class EmployeeCodeUniqueValidatorTest {
@Autowired
private Validator validator;
@MockBean
private EmployeeDao employeeDao;
...
}
For reference only, it was applied to validator-related tests with less than 30 classes and less than 500 methods, and the execution time was reduced to less than half: +1:
The test execution time will be faster, but it is a problem that you have to enumerate the classes to DI each time. While custom validator test classes have few DI components, it's pretty good, but for classes with a lot of dependencies, it may be better to prioritize maintainability over execution speed.
Also, if you are doing team development, it is difficult to unify such implementation rules, and test code that specifies only @SpringBootTest
tends to increase without your knowledge. It seems good to use ArchUnit etc. to introduce a mechanism to mechanically notice.
@Test
void validatorTestShouldeRestrictAutowiredComponent() {
classes().that().haveNameMatching(".+ValidatorTest$").and().areAnnotatedWith(SpringBootTest.class)
.should()
.beAnnotatedWith(new DescribedPredicate<>("@SpringBootTest(classes = {ValidationAutoConfiguration.class, ...}Limit the components to DI with") {
@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);
}
In this article, I showed you how to speed up unit testing of custom validators in a project using Spring Boot. I've always known that custom validators are slow to run, but I haven't been able to find this method on the internet, so I hope it's helpful for people with the same problems.
Recommended Posts