2017-12-01 90 views
11

Używam @EntityListeners do wykonywania operacji przed zapisaniem w Db i po załadowaniu. Wewnątrz mojej klasy Listener nawiązuję połączenie z Ecryptor (która potrzebuje pobrać informacje z pliku konfiguracyjnego), więc nie można wywołać statycznego szyfratora i trzeba go wstrzyknąć do mojego odbiornika. Dobrze?@EntityListeners Injection + jUnit Testowanie

Cóż, zastrzyki w EntityListeners nie mogą być wykonane od razu, ale masz kilka metod, aby to zrobić, np. Używając SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); lub nawet metody pokazanej tutaj. https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/

Fajnie, problem polega na tym, że: Żadne z rozwiązań nie obsługuje testów jednostkowych! Podczas uruchamiania testów, które zaszyfrowałem, wprowadziłem w moim modelu Listener jest zawsze null.

Tutaj SpringBeanAutowiringSupport does not inject beans in jUnit tests Istnieje rozwiązanie do stworzenia tego kontekstu i przekazania do instancji obiektu, ale to nie rozwiązuje mojego problemu, ponieważ mam "wtrysk", aby go dodać.

Jakikolwiek sposób stworzyć kontekst w moich testach i jakoś przekazać go moim słuchaczom? Jeśli nie, w jaki sposób mogę utworzyć statyczną metodę do mojego Encryptor i nadal mam dostęp do środowiska API, aby przeczytać moje właściwości?

Pakiet Listener:

public class PackageListener{ 
    @Autowired 
    Encryptor encryptor; 

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

Moje testy

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

usługa Pakiet

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

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

    return pack; 
} 

Encryptor:

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; 
    } 
    ... 
+0

Jeśli "null" nie używasz kontekstu. Twój test sprawia, że ​​zastanawiam się, czy korzystasz nawet z kontekstu stworzonego przez test (wątpię, żeby spojrzał na to, co robisz w teście). –

+0

Dziękuję za Twój komentarz @ M.Deinum, Tworzę kontekst używając '@ContextConfiguration (classes = {ApplicationConfiguration.class})' w mojej klasie 'BaseTest'. Wszystkie iniekcje i konfiguracje działają poprawnie, z wyjątkiem 'Encryptor' (wywoływanego z EntityListener) –

+0

Jak już powiedziałem, wątpię, czy faktycznie używasz tego po sposobie uzyskiwania usługi ... –

Odpowiedz

0

Można utworzyć klasę DemoApplicationContextInitializer, aby zapisać referencję appliationContext we właściwości statycznej w klasie głównej.

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

Następnie można uzyskać dostęp do kontekstu, w swojej jednostki słuchacza

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())); 
    } 
} 

i uczynić tę pracę w swoim teście junit, wystarczy dodać inicjator w teście jak to ...

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

Działa bez żadnego problemu w moim środowisku. Mam nadzieję, że będzie to również pomocne dla ciebie.

1

Aby odpowiedzieć na to, czego potrzebujesz, musisz utworzyć 2 klasy, które wykonają całą potrzebną konfigurację.

Trzeba stworzyć testConfig z najbliższych adnotacji:

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

Następnie, jeśli chcesz połączyć się z bazą danych:

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

} 

Teraz masz to wszystko skonfigurować, wystarczy aby utworzyć test importowania konfiguracji:

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

Otrzymasz kontekst wiosenny w Nitializowany z dostępem do wszystkich Twoich zasobów (MVC), DAO i Modelu.

+0

dzięki za odpowiedź. Jak wspomniałem w komentarzach, zastrzyki działają doskonale w moich testach. Mój jedyny problem dotyczy wtrysku wewnątrz obiektu JPA EntityListener podczas uruchamiania testów. –