@EntityListeners Инъекция + jUnit Тестирование

Я использую @EntityListeners для выполнения операций перед тем, как сохранить в своем Db и после загрузки. Внутри класса Listener я делаю вызов Ecryptor (который должен извлекать информацию из файла конфигурации), поэтому шифрование нельзя вызвать статически и нужно ввести в мой Listener. Правильно?

Ну, инъекции в EntityListeners не могут быть выполнены сразу, но у вас есть некоторые способы сделать это, например, используя SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); или даже метод, показанный здесь. https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/

Прохладный, проблема в том, что ни одно из решений не поддерживает модульное тестирование! При запуске тестов, которые я ввел в мою модель, в качестве хранителя всегда есть null.

Здесь SpringBeanAutowiringSupport не вставляет beans в тесты jUnit. Существует решение для создания этого контекста и перехода к экземпляру объекта, но он не решает мою проблему так как у меня есть "Инъекция", чтобы добавить к ней.

Любой способ создать контекст в моих тестах и ​​каким-то образом передать его моим слушателям? Если нет, каким-либо образом я могу создать статический метод для моего Encryptor и все еще иметь доступ к 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;
    }
    ...

Ответ 1

Вы можете создать класс 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 {
...
}

Он работает без каких-либо проблем в моей среде. Надеюсь, это будет полезно и вам.

Ответ 2

Чтобы ответить на то, что вам нужно, вам нужно создать 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 {...}

Вы получите инициализированный контекст spring с доступом ко всем вашим ресурсам (MVC) Services, DAO и Model.