2011-12-02 81 views
7

我想围绕一些审计实体建立一些测试。我的问题是envers只对事务提交进行审计。与休眠Envers集成测试

我需要创建/编辑一些测试对象,提交事务,然后检查修订。

与envers集成测试的最佳方法是什么?

更新:这是一个非常糟糕的,非确定性的测试类,我想实现的目标。我宁愿这样做,而不依赖于测试方法的顺序

首先在单个事务中创建一个帐户和account_transaction。两个经审计的条目均用于修订1.

第二次更新了新事务中的account_transaction。经审计的条目处于修订版本2.

第三,在修订版本1中加载经过审计的帐户并对其执行操作。

@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); 
    } 
+1

检查[this](http://nurkiewicz.blogspot.com/2011/11/spring-pitfalls-transactional-tests.html)出 - 无耻的自我推销。 –

+0

感谢您的回复Tomasz,我仍然不确定如何从您的博客文章解决我的问题。我对延迟加载等的误报没有任何问题,实际上提交了一些事务来设置一些审计测试数据。也许我错过了你的帖子中显而易见的东西? –

+1

那么,滚动我的文章到'DbResetRule' - 我的想法是避免使用'@ Transactional' JUnit测试,只是让你的代码提交和回滚事务。显然这使测试不可重复和脆弱。但不是回滚所做的更改,而是建议在每次测试之前/之后倾倒数据库并将其恢复。代码是在斯卡拉,但这只是一个普遍的想法。让我知道如果这是你正在寻找的,所以我将在单独的答案中详细阐述一点。 –

回答

3

根据Tomasz的建议,我在每次dao操作后都使用TransactionTemplate来实现提交。没有类级别的@Transactional注释。

在方法结束之前插入envers审计条目,这正是我所需要的。

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

我是Spring的事务测试支持的用户,它在完成时回滚测试,并且由于envers的设计,未创建修订。我创建了一个hack,看起来允许在事务提交之前“手动”告诉envers执行其工作,但允许spring继续回滚。

这些片段应该有所帮助。 1.创建您自己的auditlistener,覆盖现有的envers审计侦听器。这允许访问对单元测试可见的静态成员。可能有更好的方法,但它可行。

public class AuditEventListenerForUnitTesting extends AuditEventListener { 

    public static AuditConfiguration auditConfig; 

    @Override 
    public void initialize(Configuration cfg) { 
     super.initialize(cfg); 
     auditConfig = super.getVerCfg(); 
    } 
} 

修改您的persistence.xml包括这个新的侦听器类,而不是一个由envers提供

(重复其他听众如果有必要)

现在的“单位”测试中:

{ 
    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 
} 

我需要这个,因为我对连接到版本控制表的hibernate实体有一些JDBC查询。

0

另外两个解决方案没有为我工作,所以我用另一种方法,我只是创建一个新的事务并强制提交。每次我需要一个新的修订版本,我都会再次修改。

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

如果使用@Transactional(transactionManager="transactionManager")但它可能会绕过提交,并考虑每个测试作为一个事务(因而不相同的测试中versionning多时间......)

2

这是强烈this previous answer适应的启发与Envers 4.2.19.Final(JPA 2.0)合作。这个解决方案不需要交易来提交,这对我来说是一个需求。

首先创建以下实施org.hibernate.integrator.spi.Integrator并将其添加到类路径:

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 
    } 

} 

然后src/test/resources下创建一个META-INF/services/org.hibernate.integrator.spi.Integrator文件和积分类的完全qulified名称粘贴到其中。

在您的测试,打电话给你的DAO方法,刷新Hibernate的Session它完成后告诉Envers着手:

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

你就可以测试你的数据库的内容和最终回滚事务。