在测试时,Spring注入在自定义hibernate验证器中不起作用

Sha*_*jun 1 testng spring spring-mvc hibernate-validator

测试配置类

@Configuration
@Import( value = GlobalCoreContext.class)
public class TestConfiguration {

    @Bean
    public Validator createValidatorFactory() {
        return Validation.buildDefaultValidatorFactory().getValidator();
    }

}
Run Code Online (Sandbox Code Playgroud)

ValidatorClass

public final class LoginAttemptsExceededValidatorLoginId implements ConstraintValidator<LoginAttemptsExceeded, String> {

    @Resource
    private LoginDao loginDao;

    private LoginAttemptsExceeded loginAttemptsExceeded;

    @Override
    public void initialize(final LoginAttemptsExceeded loginAttemptsExceeded) {
        this.loginAttemptsExceeded = loginAttemptsExceeded;
    }

    /**
     * Validation fails if the login attempts have exceeded the preset number.
     * @param loginId
     * @param constraintValidatorContext
     * @return
     */
    @Override
    public boolean isValid(final String loginId, final ConstraintValidatorContext constraintValidatorContext) {
        if(StringUtility.IsEmpty(loginId)) return false;

        return !loginDao.findByLogin(loginId).isLoginAttemptsExceeded();
    }
}
Run Code Online (Sandbox Code Playgroud)

单元测试类

@ContextConfiguration(classes = TestConfiguration.class )
public class LoginAttemptsExceededValidatorLoginIdTest extends AbstractTestNGSpringContextTests {

    @Autowired
    private Validator validator;
    private PasswordChange passwordChange;
    private LoginEntity loginEntity;
    private static final String TEST_PASSWORD = "abcDEFG10";
    private static final String TEST_LOGIN = "passwordChangeValidatorTestLogin";


    @Resource
    private LoginDao loginDao;

    @BeforeMethod
    public void setUp() throws Exception {

        passwordChange = new PasswordChange(TEST_LOGIN, TEST_PASSWORD, "newPassword");

        /**
         * setup a Login record
         */
        loginEntity = DataBuilder.createLogin();
        loginEntity.setLogin(TEST_LOGIN);
        loginEntity.setPasswordHash(encryptToBytes(TEST_PASSWORD));
        loginEntity.setLoginAttempts(0);
        loginDao.saveAndFlush(loginEntity);
    }

    @AfterMethod
    public void tearDown() throws Exception {
        // Remove the login record.
        loginDao.delete(loginEntity);
        loginDao.flush();
    }

    @Test
    public void testIsValid() throws Exception {
        Set<ConstraintViolation<PasswordChange>> constraintViolations = validator.validate(passwordChange);
        assertEquals(constraintViolations.size(), 0, "When a valid username, password are supplied, the number of errors should be zero.");

    }
}
Run Code Online (Sandbox Code Playgroud)

GlobalCoreContext.java

@Configuration
@EnableJpaRepositories(basePackages = {"com.mrll.global.core.entities", "com.mrll.global.core.daos"})
@ComponentScan(basePackages = { "com.mrll.global.core.password", "com.mrll.global.core.util", "com.mrll.global.core.constraints" })
@EnableTransactionManagement
@Import({DataSourceContext.class, LiquibaseContext.class})
public class GlobalCoreContext {

    private static Logger LOG = LoggerFactory.getLogger(GlobalCoreContext.class);

    private static final String DATASOURCE_SCHEMA = "datasource.schema";
    private static final String HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    private static final String HIBERNATE_GEN_DDL = "hibernate.genddl";
    private static final String HIBERNATE_CONN_CHARSET = "hibernate.connection.charSet";
    private static final String ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";

    @Resource
    private org.springframework.core.env.Environment environment;

    @Resource
    DataSource dataSource;

    @Bean
    public EntityManager entityManger() {
        try {
            return entityManagerFactory().getObject().createEntityManager();
        } catch (ClassNotFoundException e) {
            throw new PersistenceException("unable to create entity manager", e);
        }
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws ClassNotFoundException {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setPackagesToScan(environment.getRequiredProperty(ENTITYMANAGER_PACKAGES_TO_SCAN));
        entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
        entityManagerFactoryBean.setJpaProperties(additionalProperties());
        return entityManagerFactoryBean;
    }

    @Bean
    public HibernateJpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        hibernateJpaVendorAdapter.setShowSql(Boolean.valueOf(environment.getRequiredProperty(HIBERNATE_SHOW_SQL)));
        hibernateJpaVendorAdapter.setGenerateDdl(Boolean.valueOf(environment.getRequiredProperty(HIBERNATE_GEN_DDL)));
        hibernateJpaVendorAdapter.setDatabasePlatform(environment.getRequiredProperty(HIBERNATE_DIALECT));
        return hibernateJpaVendorAdapter;
    }

    /**
     * add JPA properties, optionally adding default schema if profile's datasource is Oracle instance.
     *
     * @return Properties to add
     */
    Properties additionalProperties() {
        Properties properties = new Properties();
        properties.put(HIBERNATE_CONN_CHARSET, environment.getRequiredProperty(HIBERNATE_CONN_CHARSET));
        properties.put("hibernate.cache.use_second_level_cache", true);

        // use this one for hibernate 4.x +
//      properties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");

        // use this one for hibernate 3.2.x +
//      properties.put("hibernate.cache.region.factory_class", "net.sf.ehcache.hibernate.EhCacheRegionFactory");
//      properties.put("hibernate.cache.use_query_cache", true);
//      properties.put("hibernate.generate_statistics", true);

        if (!"H2".equalsIgnoreCase(environment.getRequiredProperty(DataSourceContext.SPRING_PROFILES_ACTIVE))) {
            properties.put("hibernate.default_schema", environment.getRequiredProperty(DATASOURCE_SCHEMA));
        }
        LOG.debug(String.format("properties=%s", properties));
        return properties;
    }

    @Bean
    public PlatformTransactionManager transactionManager() throws ClassNotFoundException {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
        transactionManager.setJpaDialect(new HibernateJpaDialect());
        return transactionManager;
    }

    @Bean
    public HibernateExceptionTranslator hibernateExceptionTranslator() {
        return new HibernateExceptionTranslator();
    }


    @Bean
    public static Constants getConstants() {
        return Constants.getInstance();
    }
}
Run Code Online (Sandbox Code Playgroud)

由于某种原因,loginDaoin 里面的inValid方法LoginAttemptsExceededValidatorLoginId为null.它在运行时运行良好.

但是在单元测试期间LoginDao,即使将javax.validation.Validator其注入UnitTest,也不会将其注入Validator .

我究竟做错了什么 ?

Har*_*rdy 7

我的猜测是你的问题在于:

@Bean
public Validator createValidatorFactory() {
    return Validation.buildDefaultValidatorFactory().getValidator();
}
Run Code Online (Sandbox Code Playgroud)

这将构建默认的Validator,包括不注入Spring bean 的默认ConstraintValidatorFactory.如果您查看文档,您将找到

默认情况下,LocalValidatorFactoryBean配置一个SpringConstraintValidatorFactory,它使用Spring创建ConstraintValidator实例.这允许您的自定义ConstraintValidators像其他任何Spring bean一样受益于依赖注入.

(见http://docs.spring.io/spring/docs/3.0.0.RC3/reference/html/ch05s07.html)

您必须确保您的Validator使用相同的约束验证器工厂.或者为什么不直接将LocalValidatorFactoryBean注入测试?如果我理解正确GlobalCoreContext是您以编程方式配置bean的地方.为什么不在那里添加LocalValidatorFactoryBean.但是,我不是春天的专家.