Integration Testing with Hibernate Envers

I am trying to create some tests around some objects being checked. My problem is that only checks are checked when a transaction is committed.

I need to create / edit some test objects, commit the transaction, and then check the changes.

What is the best approach to testing integration with envers?

Update: Here is a really bad, non-deterministic test class of what I want to achieve. I would rather do this without relying on the order of the test methods

First create an account and account_transaction in one transaction. Both verified entries relate to revision 1.

The second updated account_transaction in the new transaction. Audited recording is in edition 2.

Third, download the verified account in revision 1 and do something with it.

@Transactional @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"/testApplicationContext.xml"}) public class TestAuditing { @Autowired private AccountDao accountDao; @PersistenceContext private EntityManager entityManager; @Test @Rollback(false) public void first() { Account account = account("Test Account", "xxxxxxxx", "xxxxxx"); AccountTransaction transaction = transaction(new Date(), Deposit, 100, "Deposit"); account.setTransactions(newArrayList(transaction)); accountDao.create(account); } @Test @Rollback(false) public void second() { Account account = accountDao.getById(1L); AccountTransaction transaction = account.getTransactions().get(0); transaction.setDescription("Updated Transaction"); accountDao.update(account); } @Test public void third() { AuditReader reader = AuditReaderFactory.get(entityManager); List<Number> accountRevisions = reader.getRevisions(Account.class, 1L); //One revision [1] List<Number> transactionRevisions = reader.getRevisions(AccountTransaction.class, 1L); //Two revisions [1, 2] Account currentAccount = accountDao.getById(1L); Account revisionAccount = (Account) reader.createQuery().forEntitiesAtRevision(Account.class, 1).getSingleResult(); System.out.println(revisionAccount); } 
+7
source share
4 answers

I am a user of Spring Transactional Testing Support, which rolls back tests when they are done, and due to the design of changes, revisions are not created. I created a hack that apparently allows developers to β€œtell” developers to do their work manually before a transaction is made, but allows Spring to continue rolling back.

These fragments should help. 1. Create your own auditor who overrides the existing audit auditor. This allows you to access the static element, visible for unit tests. There is probably a better way, but it works.

 public class AuditEventListenerForUnitTesting extends AuditEventListener { public static AuditConfiguration auditConfig; @Override public void initialize(Configuration cfg) { super.initialize(cfg); auditConfig = super.getVerCfg(); } } 

modify your persistence.xml to include this new listener class instead of the one provided by envers

(repeat for other listeners, if necessary)

Now in the "unit" test:

 { saveNewList(owner); //code that does usual entity creation em.flush(); EventSource hibSession = (EventSource) em.getDelegate(); AuditEventListenerForUnitTesting.auditConfig.getSyncManager().get(hibSession).doBeforeTransactionCompletion(hibSession); //look for envers revisions now, and they should be there } 

I need this because I have some JDBC queries for hibernation objects related to version control tables.

+4
source

According to Tomasz's suggestion, I used TransactionTemplate to reach commits after every dao operation. No @Transactional annotation at class level.

Inclusion audit records are entered before the method I need is completed.

 @ContextConfiguration("testApplicationContext.xml") public class TestAuditing extends AbstractJUnit4SpringContextTests { @Autowired private PlatformTransactionManager platformTransactionManager; @Autowired private PersonDao personDao; private TransactionTemplate template; @Before public void transactionTemplate() { template = new TransactionTemplate(platformTransactionManager); } @Test public void test() { Person person = createInTransaction(person("Karl", "Walsh", address("Middle of nowhere")), personDao); System.out.println(person); } private <T> T createInTransaction(final T object, final Dao<?, T> dao) { return template.execute(new TransactionCallback<T>() { public T doInTransaction(TransactionStatus transactionStatus) { dao.create(object); return object; } }); } } 
+3
source

This is greatly inspired by this previous answer , adapted to work with Envers 4.2.19.Final (JPA 2.0). This solution does not require a transaction to commit, which was a requirement in my case.

First, create the following org.hibernate.integrator.spi.Integrator implementation and add it to the classpath:

 public class MyIntegrator implements Integrator { public static AuditConfiguration auditConfig; @Override public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { auditConfig = AuditConfiguration.getFor(configuration); } @Override public void integrate(MetadataImplementor metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { // NOP } @Override public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { // NOP } } 

then create the META-INF/services/org.hibernate.integrator.spi.Integrator under src/test/resources and paste the fully qulified integrator class name into it.

In your test, call your DAO method, start a sleep session, and then tell Envers:

 EventSource es = (EventSource) entityManager.getDelegate(); SessionImplementor si = entityManager.unwrap(SessionImplementor.class); MyIntegrator.auditConfig.getSyncManager().get(es).doBeforeTransactionCompletion(si); 

you can check the contents of your database and finally cancel the transaction.

+2
source

Two other solutions did not work for me, so I used a different way, I just create a new transaction and force commit. Every time I need a new revision, I do it again.

 @Autowired @Qualifier("transactionManager") private PlatformTransactionManager platformTransactionManager; @Test public void enversTest() throws Exception{ Entity myEntity = new Entity(); TransactionStatus status = platformTransactionManager.getTransaction(new DefaultTransactionDefinition()); myEntity.setName("oldName"); myEntity = entityDao.merge(myEntity); platformTransactionManager.commit(status); // creates a revision with oldname TransactionStatus newStatus = platformTransactionManager.getTransaction(new DefaultTransactionDefinition()); myEntity.setName("newName"); myEntity = entityDao.merge(myEntity); platformTransactionManager.commit(newStatus); // creates a new revision with newName } 

If you use @Transactional(transactionManager="transactionManager") , however it can bypass the commits and treat each test as a single transaction (thus, not repeating several times during the same test ...)

0
source

All Articles