I use @EntityListeners
to make operations before I save in my Db and after I load.
Inside my Listener class I make a call to an Ecryptor
(which needs to fetch info from configuration file), so the encryptor can't be called statically and need to be injected in my Listener. Right?
Well, injections in EntityListeners can't be done straight away, but you have some methods to do that, like using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
or even the method showed here. https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/
Cool, the problem is: None of the solutions support unit testing! When running tests that encryptor I had injected in my model Listener is always null
.
Here SpringBeanAutowiringSupport does not inject beans in jUnit tests There is a solution to create this context and pass to a instantiated object, but it does not solve my problem since I have the "Injection" to add to it.
Any way to create a context in my tests and somehow pass it to my listeners? If not, any way I can create a static method to my Encryptor and still have access to the Environment API to read my properties?
Package Listener:
public class PackageListener{
@Autowired
Encryptor encryptor;
@PrePersist
public void preSave(final Package pack){
pack.setBic(encryptor.encrypt(pack.getBic()));
}
...
My test
@Test
@WithuserElectronics
public void testIfCanGetPackageById() throws PackageNotFoundException{
Package pack = packagesServiceFactory.getPackageService().getPackage(4000000002L);
}
Package service
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;
}
...
The idea of dependency injection inside our tests is not something new; it is afforded by spring’s test utilities and it’s not that hard to implement for your general case. The following code snippet will show how you can create a custom JUnit 4 Runner to enable field injection inside your tests.
Injecting parameters into your test methods could be done using the JUnit 4 API, but it was fairly limited. With JUnit 5, the Jupiter API can be extended – by implementing ParameterResolver – to serve up objects of any type to your test methods.
JUnit Test. By convention, I’m naming my Integration Test with the suffix of ‘IT’. Traditionally, you will name your unit tests with the suffix of ‘Test’ or ‘Tests’, and your integration tests with the suffix of ‘IT’. This does not affect how JUnit runs the tests.
Separation of concerns between the unit test and the data that drives it Reuse, should other unit tests require valid Person objects to drive them If the type of parameter is Person, then the extension tells the JUnit platform that it supports that parameter type, otherwise it returns false, saying it does not.
You can create a DemoApplicationContextInitializer
class to store the appliationContext
reference in a static property in your main class.
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);
}
}
Then you can access the context in your entity listener
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()));
}
}
And to make this work in your junit test, just add the initializer in your test like this ...
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, classes = Application.class)
@ContextConfiguration(classes = Application.class, initializers = DemoApplicationContextInitializer.class)
public class MyTest {
...
}
It works without any issue in my environment. Hope it will be helpful to you too.
To answer what you need, you have to create 2 classes that will do all the configuration needed.
You have to create a testConfig with the next annotations:
@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;
}
}
Then if you want to connect with your database:
@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;
}
}
Now you have it all set up, you just need to create your test importing your configurations:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class)
public class TestCase {...}
You will get your spring context initialized with access to all your resources (MVC) Services, DAO and Model.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With