【问题标题】:@EntityListeners Injection + jUnit Testing@EntityListeners 注入 + jUnit 测试
【发布时间】:2018-05-15 09:54:45
【问题描述】:

我使用@EntityListeners 在保存到我的数据库之前和加载之后进行操作。 在我的 Listener 类中,我调用了 Ecryptor(它需要从配置文件中获取信息),因此不能静态调用加密器,需要将其注入到我的 Listener 中。对吧?

好吧,EntityListeners 中的注入不能立即完成,但您有一些方法可以做到这一点,例如使用 SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); 甚至这里显示的方法。 https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/

很酷,问题是:没有一个解决方案支持单元测试!在运行我在模型监听器中注入的加密器的测试时,总是null

这里SpringBeanAutowiringSupport does not inject beans in jUnit tests有一个解决方案来创建这个上下文并传递给一个实例化的对象,但它并没有解决我的问题,因为我有“注入”要添加到它。

有什么方法可以在我的测试中创建上下文并以某种方式将其传递给我的听众? 如果没有,我有什么方法可以为我的加密器创建一个静态方法,并且仍然可以访问 Environment API 来读取我的属性?

包监听器:

public class PackageListener{
   @Autowired
   Encryptor encryptor;

   @PrePersist
   public void preSave(final Package pack){
      pack.setBic(encryptor.encrypt(pack.getBic()));
   }
   ...

我的测试

 @Test
 @WithuserElectronics
 public void testIfCanGetPackageById() throws PackageNotFoundException{
     Package pack = packagesServiceFactory.getPackageService().getPackage(4000000002L);
 }

打包服务

  public Package getPackage(Long id) throws PackageNotFoundException{
    Package pack = packageDao.find(id);

    if (pack == null) {
        throw new PackageNotFoundException(id);
    }

    return pack;
}

加密器:

public class Encryptor{
    private String salt;

    public Encryptor(String salt){
        this.salt = salt;
    }

    public String encrypt(String string){
        String key = this.md5(salt);
        String iv = this.md5(this.md5(salt));
        if (string != null) {
            return encryptWithAesCBC(string, key, iv);
        }
        return string;
    }
    ...

【问题讨论】:

  • 如果是null,您没有使用上下文。您的测试让我想知道您是否甚至在使用测试创建的上下文(我怀疑它是否会查看您在测试中所做的事情)。
  • 感谢您的评论 @M.Deinum,我正在我的 BaseTest 类中使用 @ContextConfiguration(classes = {ApplicationConfiguration.class}) 创建一个上下文。除了Encryptor(从 EntityListener 调用)之外,所有注入和配置都正常工作
  • 如前所述,我怀疑您是否真的在使用它来获取服务...
  • 再次感谢!我明白你在说什么,但是,即使我将上下文传递给服务,之后我应该如何将它传递给加密器?
  • 你不应该传递那些东西......我只是说你在测试中所做的事情与在实际代码中不同。您有某种服务工厂来获得测试服务这一事实暗示了我。您应该将依赖项注入到您的测试用例中,而不是使用其他对象来获取它们。但是这里的代码太少了,这里的东西充其量只是一个猜谜游戏。

标签: java spring-mvc junit dependency-injection entitylisteners


【解决方案1】:

要满足您的需求,您必须创建 2 个类来完成所需的所有配置。

您必须使用下一个注释创建一个 testConfig:

@Configuration
@ComponentScan(basePackages = { "yourPath.services.*",
        "yourPath.dao.*" })
@EnableAspectJAutoProxy
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "yourPath.dao.entities", 
    entityManagerFactoryRef = "entityManagerFactory", 
    transactionManagerRef = "transactionManager", 
    repositoryBaseClass = Dao.class)
@Import({ DataSourceConfig.class }) //Explained below
public class TestConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public List<String> modelJPA() {
        return Collections.singletonList("es.carm.sms.ortopedia.entities");
    }

    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactory.setPackagesToScan(modelJPA().toArray(new String[modelJPA().size()]));
        entityManagerFactory.setDataSource(this.dataSource);
        JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
        return entityManagerFactory;
    }
}

那么如果你想连接你的数据库:

@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
        dataSource.setUrl("jdbc:oracle:thin:@ip:port:sid");
        dataSource.setUsername("name");
        dataSource.setPassword("pass");
        return dataSource;
    }

}

现在你已经设置好了,你只需要创建你的测试导入你的配置:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class)
public class TestCase {...}

您将通过访问所有资源 (MVC) 服务、DAO 和模型来初始化您的 spring 上下文。

【讨论】:

  • 感谢您的回答。正如我在 cmets 中提到的,注射在我的测试中非常有效。我唯一的问题是运行测试时 JPA EntityListener 中的注入。
【解决方案2】:

您可以创建一个DemoApplicationContextInitializer 类来将appliationContext 引用存储在主类的静态属性中。

public class DemoApplicationContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext ac) {
        Application.context = ac;
    }
}


@SpringBootApplication
public class Application {

    public static ApplicationContext context;

    public static void main(String[] args) throws Exception {
        new SpringApplicationBuilder(Application.class)
        .initializers(new DemoApplicationContextInitializer())
        .run(args);
    }
}

然后你可以在你的实体监听器中访问上下文

public class PackageListener{
   //@Autowired
   Encryptor encryptor;

   @PrePersist
   public void preSave(final Package pack){
      encryptor = Application.context.getBean(Encryptor.class);
      pack.setBic(encryptor.encrypt(pack.getBic()));
   }
}

为了在你的 junit 测试中工作,只需像这样在你的测试中添加初始化器......

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, classes = Application.class)
@ContextConfiguration(classes = Application.class, initializers = DemoApplicationContextInitializer.class)
public class MyTest {
...
}

它在我的环境中没有任何问题。希望对你也有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    • 1970-01-01
    • 2014-04-05
    • 2014-02-28
    • 2011-08-03
    • 2015-11-15
    相关资源
    最近更新 更多