如何在 JUnit 测试中使用 Spring defaultValidator

Posted

技术标签:

【中文标题】如何在 JUnit 测试中使用 Spring defaultValidator【英文标题】:How to use Spring defaultValidator in JUnit tests 【发布时间】:2022-01-13 06:08:12 【问题描述】:

我一直在我的测试类上使用@SpringJunitConfig 来减少@SpringBootTest 的上下文加载时间。这在我只使用自己的类时效果很好,因为我可以轻松指定要加载的包/类。

现在我正在尝试使用 Spring 的默认参数验证。基于其他 SO 答案,我创建并加载了 defaultValidator bean。但是,当我的测试调用带有验证的方法并且测试失败时,不会触发 Spring 的默认验证。我知道被测类上的注释是正确的,因为当我切换到@SpringBootTest 时,测试通过了。

还有什么想法吗?

这是我最接近的一次,但 Spring 不会自动验证,除非我切换到 @SpringBootTest,它会加载完整的上下文并且速度太慢。

测试类

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

服务类

@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

【问题讨论】:

@xerx593 是的。将服务类添加到问题中。 完全没有(预期的)异常?或错误的类型? (测试失败的确切原因是什么?) 【参考方案1】:

defaultValidator bean 由 spring-boot 自动配置内容定义,如果有任何带有 @EnableAutoConfiguration 注释的 @Configuration bean 将启用。

@EnableAutoConfiguration 默认情况下会考虑所有由 spring-boot 定义的自动配置内容(即那些在spring.factories 中定义的内容),这对于编写单元测试来说可能太多了。所以还有另一个注解叫做@ImportAutoConfiguration,它可以只导入和应用一个指定的自动配置类。

由于 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();
        
    

 

提示:要找出哪个自动配置类定义了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)

【讨论】:

出色的答案,尤其是提示,因为它可以帮助我更好地了解如何在未来解决类似的需求!

以上是关于如何在 JUnit 测试中使用 Spring defaultValidator的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Mockito 和 JUnit 在 Spring Boot 中测试 POST 方法

如何在使用 @RunWith 和 @ContextConfiguration 注释的 jUnit 测试中访问 Spring 上下文?

如何在单个 JUnit 测试中运行两个 Spring Boot 微服务?

如何在 Spring Boot 和 JUnit 测试中定义 spring.config.location?

如何在用 Kotlin 编写的 JUnit 5 测试类中注入 Spring bean?

如何使用 JUnit 测试 Spring Boot aspectj 方法?