How to have DBUnit #DatabaseSetup to happen before spring autowiring? - spring

Consider the typical DBUnit Spring Test (see https://github.com/springtestdbunit/spring-test-dbunit) :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:/META-INF/spring/applicationContext-database.xml",
"classpath:spring-*.xml"
})
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
#DatabaseSetup("/dbunit/data.xml")
public class UnitTest {
#Autowired
private UnitUnderTest uut;
#Test
public void shouldInitDB() {
...
}
}
What I have verified is that, and has expected, Autowiring will happen before DatabaseSetup.
This must happen because DBUnit depends on the application context to provide the configured data source.
The problem is that the UnitUnderTest bean has a #PostConstruct where it loads some data from the DB but, since the Autowiring happens before the DBunit setup, the data will not be available at this stage.
Any ideas on how to solve this issue in a clean way?

You can you Spring's ResourceDatabasePopulator.
I think you can use something like this
#PostConstruct
public void myInMemryPopulator() {
final ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
try {
Resource[] array = resourceResolver.getResources("classpath:/*.sql");
for (Resource resource : array) {
databasePopulator.addScript(resource);
}
databasePopulator.populate(dataSource.getConnection());
} catch (IOException | SQLException e) {
LOGGER.error("Error in databasePopulator {} ", e);
}
}

You can have a setup method in your test class and call the post construct method manually. That will work.

Lazy initialization helped me, I added #Lazy over the #Component* and over the #Autowired in injection points
#Lazy
#Component
public class UnitUnderTestImpl {
...
}
#Lazy
#Autowired
private UnitUnderTest uut;

Related

Spring 2 + JUnit 5, share #MockBean for entire test suite

I create a Spring 2.3 application using Spring Data REST, Hibernate, Mysql.
I created my tests, I've around 450 tests splitted in about 70 files. Because the persistence layer leans on a multi tenant approach (single db per tenant) using a Hikari connection pool, I've the need to avoid the pool is initializated for each test file but at the same time I need to use #MockBean because I need to mock up some repositories in the entire Spring test contest.
I create a custom annotation for all test in my suite:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#SpringBootTest
#TestExecutionListeners(value = TestExecutionListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
#Transactional
#ActiveProfiles("test")
public #interface TestConfig {
}
Reading many posts and the doc, I know if I use #MockBean inside a test, the Spring context is reloaded and therefore a new pool connection is created in my case.
My idea is to create a #MockBean and share it with all tests in my suite so the context is not reloaded every time.
I tried several approaches:
#Log4j2
public class TestExecutionListener extends AbstractTestExecutionListener implements Ordered {
#Override
public void beforeTestMethod(TestContext testContext) throws Exception {
try {
TestDbUtils testDbUtils = (TestDbUtils) testContext.getApplicationContext().getBean(TestDbUtils.class);
testDbUtils.truncateDB();
TenantRepository tenantRepository = mock(TenantRepository.class);
testContext.setAttribute("tenantRepository", tenantRepository);
TenantContext.setCurrentTenantId("test");
when(tenantRepository.findByTenantId("test")).thenReturn(testDbUtils.fakeTenant());
} catch (Exception e) {
}
}
#Override
public int getOrder() {
return Integer.MAX_VALUE;
}
}
All my tests are annotated like this:
#TestConfig
#Log4j2
public class InvoiceTests {
#Test
public void test1(){
}
}
Unfortunately my tenantRepository.findByTenantId() is not mocked up. I also tried to create an abstract superclass:
#SpringBootTest
#TestPropertySource(locations = "classpath:application-test.properties")
#TestExecutionListeners(value = TestExecutionListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
#Transactional
#ActiveProfiles("test")
public abstract class AbstractIntegrationTest {
#MockBean
protected TenantRepository tenantRepository;
#MockBean
protected SubscriptionRepository subscriptionRepository;
#Autowired
protected TestDbUtils testDbUtils;
#BeforeAll
public void beforeAll() {
when(tenantRepository.findByTenantId("test")).thenReturn(testDbUtils.fakeTenant());
}
#BeforeEach
public void setup() {
testDbUtils.truncateDB();
TenantContext.setCurrentTenantId("test");
}
}
Even if my tests extended this superclass, during the run all of them were skipped (not sure why).
Is there any way to accomplish what I described?

Cannot configure #Transaction to work with Spring Data Neo4j

I'm trying to move away from manually-managed transactions to annotation based transactions in my Neo4j application.
I've prepared annotation-based Spring configuration file:
#Configuration
#EnableNeo4jRepositories("xxx.yyy.neo4jplanetspersistence.repositories")
#ComponentScan(basePackages = "xxx.yyy")
#EnableTransactionManagement
public class SpringDataConfiguration extends Neo4jConfiguration
implements TransactionManagementConfigurer{
public SpringDataConfiguration() {
super();
setBasePackage(new String[] {"xxx.yyy.neo4jplanetspojos"});
}
#Bean
public GraphDBFactory graphDBFactory(){
GraphDBFactory graphDBFactory = new GraphDBFactory();
return graphDBFactory;
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return graphDBFactory().getTestGraphDB(); //new GraphDatabaseFactory().newEmbeddedDatabase inside
}
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return neo4jTransactionManager(graphDatabaseService());
}
}
I've marked my repositories with #Transactional:
#Transactional
public interface AstronomicalObjectRepo extends
GraphRepository<AstronomicalObject>{
}
I've marked my unit test classes and test methods with #Transactional and commented old code that used to manually manage transactions:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringDataConfiguration.class},
loader = AnnotationConfigContextLoader.class)
#Transactional
public class AstronomicalObjectRepoTest {
#Autowired
private AstronomicalObjectRepo repo;
#Autowired
private Neo4jTemplate neo4jTemplate;
(...)
#Test #Transactional
public void testSaveAndGet() {
//try (Transaction tx =
//neo4jTemplate.getGraphDatabaseService().beginTx()) {
AstronomicalObject ceres = new AstronomicalObject("Ceres",
1.8986e27, 142984000, 9.925);
repo.save(ceres); //<- BANG! Exception here
(...)
//tx.success();
//}
}
After that change the tests do not pass.
I receive:
org.springframework.dao.InvalidDataAccessApiUsageException: nested exception is org.neo4j.graphdb.NotInTransactionException
I have tried many different things (explicitly naming transaction manager in #Transactional annotation, changing mode in #EnableTransactionManagment...), nothing helped.
Will be very grateful for a clue about what I'm doing wrong.
Thanks in advance!
I found the reason...
SDN does not support newest Neo4j in the terms of transaction.
I believe it is because SpringTransactionManager in neo4j-kernel has gone in 2.2+ releases, but not 100% sure.
On github we can see that 7 hours ago the change was made to fix it:
https://github.com/spring-projects/spring-data-neo4j/blob/master/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/JtaTransactionManagerFactoryBean.java
A quick fix that worked for me was to override neo4jTransactionManager method from Neo4jConfiguration in my configuration, using Neo4jEmbeddedTransactionManager class:
#Override
public PlatformTransactionManager neo4jTransactionManager(GraphDatabaseService graphDatabaseService) {
Neo4jEmbeddedTransactionManager newTxMgr = new Neo4jEmbeddedTransactionManager(graphDatabaseService());
UserTransaction userTransaction = new UserTransactionAdapter( newTxMgr );
return new JtaTransactionManager( userTransaction, newTxMgr );
}

Spring - get EntityManager from #Configuration class

I'm using Spring + Jpa and I'd like to have EntityManager into my #Configuration class.
Now my class is something like this:
#Configuration
#PropertySource("classpath:base.properties")
public class Config {
private static final Logger log = Logger.getLogger(Config.class);
#Bean
public SpringContextManager contextManager() {
return new SpringContextManager(new DefaultApplication());
}
#Bean(initMethod = "start", destroyMethod = "stop")
public ServerSession serverSession() throws Exception {
try {
ServerSession serverSession = new ServerSession(urlGateway, useSsl, hostGateway, portGateway);
serverSession.setDefaultTimeToLive(5000);
return serverSession;
} catch (Throwable e) {
log.error("", e);
return null;
}
}
#Bean
public PluginManager pluginManager() {
PluginManager pluginManager = new PluginManager();
ThreadLocalManager.set(pluginManager);
return pluginManager;
}
I know that I can't add #PersistenceContext to #Configuration class, so I don't know how to get entityManager at this point.
The goal of this is have entityManager asap the app start because I need to set it into a ThreadLocal class ( i need this class to use entityManager inside a JPA entitylistener where inject of persistenceContext don't work).
Now I'm getting the entityManager from a service annotated with #Service but it would be cleaner to made this settings into #Configuration class. Seems more clean.
Thanks for your help.
I found a nice example to solve my problem. This is the link of the tutorial: link

Why does Springs #Required not work when configured by annotations

I wonder if there is a way to get #Required working when doing the configuration by annotations. I turned my configuration up-and-down and back again but nothing seems to work for me. I'm using Spring 3.1
My basic configuration looks like this:
#Configuration
public class SpringConfig {
#Bean
public MailSender mailSender() {
MailSender MailSender = new MailSender();
// mailSender.setBean(dlMailSender);
return mailSender;
}
#Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
// setting som props
return myBean;
}
}
MailSender is here:
#Configurable
public class MailSender {
private MyBean myBean;
#Required
public void setMyBean(MyBean myBean) {
this.myBean = myBean;
}
}
I'm testing it with this junit:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SpringConfig.class }, loader = AnnotationConfigContextLoader.class)
public class MailSenderTest {
#Test
public void test_main_beans_exists() {
// when then given
}
}
Thanks for any help
Short answer - this is not even theoretically possible.
When using XML-based, bean definitions with their dependencies are completely managed by application context. Spring is able to check, what is being set and what is not being set.
When using annotation-based configuration, you are setting the dependencies yourself. There is no way how Spring can even know what you are doing with the bean before returning it from the factory method.
If you want to check whether the bean is correctly initialized, use InitializingBean or #PostConstruct and implement self-checking method. Spring is doing this regularly in its own beans.

Why won't the transaction start in my junit test cases?

I have a Spring 3.1 MVC + Hibernate 3.6 project with its junit4 test suit. My problem is that there is no transaction starting in my test cases, even thought I added a #Transactional.
My test case calls a controller and a dao. In the controller, a transaction is started anyway, so it does not complain. In the dao, I added a #Transactional(propagation = Propagation.MANDATORY) to be sure it will take the test's transaction. And currently it raises an IllegalTransactionStateException, which I guess it means there is no current transaction.
I tried to create programmaticaly an transaction and it does work, which means the AOP proxy to get the dao service is not the cause of the problem. However I want to create a transaction with the #Transactional annotation.
here's my dao:
// ...imports...
#Repository("taskDao")
#Transactional(propagation = Propagation.MANDATORY)
public class TaskHome implements TaskDao {
private static final Log log = LogFactory.getLog(TaskHome.class);
private SessionFactory sessionFactory;
#Autowired
public TaskHome(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Task findById(int id) {
log.debug("getting Task instance with id: " + id);
try {
Task instance = (Task) this.sessionFactory.getCurrentSession().get(
Task.class, id); // exception raised here!
if (instance == null) {
log.debug("get successful, no instance found");
} else {
log.debug("get successful, instance found");
}
return instance;
} catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
...
}
Here's my test case:
// ...imports...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class TestTaskController {
private static ClassPathXmlApplicationContext context;
private static TaskDao taskDao;
#BeforeClass
public static void initHibernate() throws Exception {
context = new ClassPathXmlApplicationContext("applicationContext.xml");
taskDao = context.getBean("taskDao", TaskDao.class);
}
#Test
public void testOnSubmit() {
// expects an existing default transaction here
Task task = taskDao.findById(1); // fails already here
// ... calls the controller and does some tests.
}
}
I searched in all Spring's documentation and googled it in any way I could imagine, but I don't see why the transaction is not started.
Any help is very welcome.
When using #RunWith(SpringJUnit4ClassRunner.class) you should obtain beans from the application context created by SpringJUnit4ClassRunner rather than from your own one.
In your case things go wrong because #Transactional on the unit test creates a transaction in the application context managed by SpringJUnit4ClassRunner, but you call methods on the beans obtained from the application context created manually.
So, remove your #BeforeClass method and obtain TaskDao via autowiring:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class TestTaskController {
#Autowired
private TaskDao taskDao;
...
}
See also:
9.3.5 Spring TestContext Framework

Resources