如何在 JUnit 测试中使用 Spring defaultValidator
How to use Spring defaultValidator in JUnit tests
我一直在我的测试 class 中使用 @SpringJunitConfig
来减少 @SpringBootTest
上的上下文加载时间。这在我只使用我自己的 classes 时效果很好,因为我可以轻松指定要加载的包/classes。
现在我正在尝试使用 Spring 的默认参数验证。根据其他 SO 答案,我创建并加载了 defaultValidator
bean。但是,当我的测试调用带有验证的方法并且测试失败时,Spring 的默认验证不会被触发。我知道被测 class 上的注释是正确的,因为当我切换到 @SpringBootTest
时,测试通过了。
还有其他想法吗?
这是我最接近的一次,但 Spring 没有自动验证,除非我切换到 @SpringBootTest
,它加载完整的上下文并且速度太慢。
测试Class
@SpringJUnitConfig()
class UserServiceImplTest {
@Configuration
@ComponentScan(basePackages = { "com.user" },
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { UserMapper.class,
UserServiceImpl.class}), useDefaultFilters = false)
static class ConfigMe {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public Validator defaultValidator() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
return factory.getValidator();
}
}
@Autowired
private UserServiceImpl userService;
@Autowired
private UserMapper mapper;
@Autowired
@Qualifier("defaultValidator")
Validator validator;
@MockBean
private UserDao userDao;
// tests for UserServiceImpl that require valdiation of method arguments
// e.g. public UserDto findUser(@NotNull @Size(min = 30, max = 30) String userUnique)
@Test
void givenInvalidUnique_whenFind_thenConstraintException() {
assertThrows(ConstraintViolationException.class, () -> {
userService.findUser(null); // null
});
}
服务Class
@Service
@Transactional
@Validated
public class UserServiceImpl implements UserService {
@Override
@Transactional(readOnly = true)
public UserDto findUser(@NotNull @Size(min = 30, max = 30) String userUnique) {
log.trace("findUser called with unique [{}]", userUnique);
Optional<User> foundUser = userDao.findByUserUnique(userUnique);
if (foundUser.isEmpty())
throw new MyEntityNotFoundException(String.format("Could not find user with unique of [%s]", userUnique));
return mapper.UserEntityToDto(foundUser.get());
}
// other service methods
}
defaultValidator
bean 由 spring-boot 自动配置内容定义,如果有任何 @Configuration
bean 被注释为 @EnableAutoConfiguration
,它将被启用。
但是 @EnableAutoConfiguration
默认情况下会考虑所有由 spring-boot 定义的自动配置内容(即那些在 spring.factories
中定义的内容),这对于编写一个单元来说可能太多了测试。所以还有另一个名为 @ImportAutoConfiguration
的注释,它可以只导入并应用指定的自动配置 class.
由于 defaultValidator
bean 是在 ValidationAutoConfiguration
中定义的,这意味着您可以简单地通过以下方式导入它:
@SpringJUnitConfig
class UserServiceImplTest {
@Configuration
@ComponentScan(basePackages = { "com.user" }, includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { UserMapper.class, UserServiceImpl.class}), useDefaultFilters = false)
@ImportAutoConfiguration(ValidationAutoConfiguration.class)
static class ConfigMe {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
}
Tips : 要找出哪个自动配置 class 定义了 defaultValidator
bean ,你可以在 application.properties
中启用调试模式 :
debug=true
然后像往常一样启动spring-boot应用程序,它将打印出以下条件评估报告:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
ValidationAutoConfiguration matched:
- @ConditionalOnClass found required class 'javax.validation.executable.ExecutableValidator' (OnClassCondition)
- @ConditionalOnResource found location classpath:META-INF/services/javax.validation.spi.ValidationProvider (OnResourceCondition)
ValidationAutoConfiguration#defaultValidator matched:
- @ConditionalOnMissingBean (types: javax.validation.Validator; SearchStrategy: all) did not find any beans (OnBeanCondition)
我一直在我的测试 class 中使用 @SpringJunitConfig
来减少 @SpringBootTest
上的上下文加载时间。这在我只使用我自己的 classes 时效果很好,因为我可以轻松指定要加载的包/classes。
现在我正在尝试使用 Spring 的默认参数验证。根据其他 SO 答案,我创建并加载了 defaultValidator
bean。但是,当我的测试调用带有验证的方法并且测试失败时,Spring 的默认验证不会被触发。我知道被测 class 上的注释是正确的,因为当我切换到 @SpringBootTest
时,测试通过了。
还有其他想法吗?
这是我最接近的一次,但 Spring 没有自动验证,除非我切换到 @SpringBootTest
,它加载完整的上下文并且速度太慢。
测试Class
@SpringJUnitConfig()
class UserServiceImplTest {
@Configuration
@ComponentScan(basePackages = { "com.user" },
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { UserMapper.class,
UserServiceImpl.class}), useDefaultFilters = false)
static class ConfigMe {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public Validator defaultValidator() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
return factory.getValidator();
}
}
@Autowired
private UserServiceImpl userService;
@Autowired
private UserMapper mapper;
@Autowired
@Qualifier("defaultValidator")
Validator validator;
@MockBean
private UserDao userDao;
// tests for UserServiceImpl that require valdiation of method arguments
// e.g. public UserDto findUser(@NotNull @Size(min = 30, max = 30) String userUnique)
@Test
void givenInvalidUnique_whenFind_thenConstraintException() {
assertThrows(ConstraintViolationException.class, () -> {
userService.findUser(null); // null
});
}
服务Class
@Service
@Transactional
@Validated
public class UserServiceImpl implements UserService {
@Override
@Transactional(readOnly = true)
public UserDto findUser(@NotNull @Size(min = 30, max = 30) String userUnique) {
log.trace("findUser called with unique [{}]", userUnique);
Optional<User> foundUser = userDao.findByUserUnique(userUnique);
if (foundUser.isEmpty())
throw new MyEntityNotFoundException(String.format("Could not find user with unique of [%s]", userUnique));
return mapper.UserEntityToDto(foundUser.get());
}
// other service methods
}
defaultValidator
bean 由 spring-boot 自动配置内容定义,如果有任何 @Configuration
bean 被注释为 @EnableAutoConfiguration
,它将被启用。
但是 @EnableAutoConfiguration
默认情况下会考虑所有由 spring-boot 定义的自动配置内容(即那些在 spring.factories
中定义的内容),这对于编写一个单元来说可能太多了测试。所以还有另一个名为 @ImportAutoConfiguration
的注释,它可以只导入并应用指定的自动配置 class.
由于 defaultValidator
bean 是在 ValidationAutoConfiguration
中定义的,这意味着您可以简单地通过以下方式导入它:
@SpringJUnitConfig
class UserServiceImplTest {
@Configuration
@ComponentScan(basePackages = { "com.user" }, includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { UserMapper.class, UserServiceImpl.class}), useDefaultFilters = false)
@ImportAutoConfiguration(ValidationAutoConfiguration.class)
static class ConfigMe {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
}
Tips : 要找出哪个自动配置 class 定义了 defaultValidator
bean ,你可以在 application.properties
中启用调试模式 :
debug=true
然后像往常一样启动spring-boot应用程序,它将打印出以下条件评估报告:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
ValidationAutoConfiguration matched:
- @ConditionalOnClass found required class 'javax.validation.executable.ExecutableValidator' (OnClassCondition)
- @ConditionalOnResource found location classpath:META-INF/services/javax.validation.spi.ValidationProvider (OnResourceCondition)
ValidationAutoConfiguration#defaultValidator matched:
- @ConditionalOnMissingBean (types: javax.validation.Validator; SearchStrategy: all) did not find any beans (OnBeanCondition)