10

我正在尝试围绕一些经过审计的实体进行一些测试。我的问题是 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);
    }
4

5 回答 5

5

我是 Spring 的事务测试支持的用户,它在测试完成后回滚测试,并且由于环境的设计,没有创建修订。我创建了一个 hack,它似乎允许在事务提交之前“告诉”envers 手动完成其工作,但允许 spring 继续回滚。

这些片段应该会有所帮助。1. 创建您自己的审计监听器,覆盖现有的 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
}

我需要这个,因为我有一些针对加入版本控制表的休眠实体的 JDBC 查询。

于 2012-07-20T00:53:35.273 回答
4

As per Tomasz's suggestion I used a TransactionTemplate to achieve the commits after each dao operation. There is no class-level @Transactional annotation.

The envers audit entries are inserted before the method finishes which is just what I needed.

@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;
            }
        });
    }
}
于 2011-12-03T20:04:13.353 回答
2

这受到适用于 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
  }

}

然后在下面创建一个META-INF/services/org.hibernate.integrator.spi.Integrator文件src/test/resources并将集成器类的完全限定名称粘贴到其中。

在您的测试中,调用您的 DAO 方法,刷新休眠会话,并在完成后告诉 Envers 继续:

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

然后您可以测试数据库的内容并最终回滚事务。

于 2016-04-01T13:05:04.713 回答
1

@thomas-naskali对Envers 的回答的更新版本5.4.15.Final

    protected void flushEnvers()
    {
        final EventSource eventSource = entityManager.unwrap(EventSource.class);
        final Session session = entityManager.unwrap(Session.class);
        final SessionImplementor sessionImplementor = entityManager.unwrap(SessionImplementor.class);

        final EnversService service = session.getSessionFactory()
            .getSessionFactoryOptions()
            .getServiceRegistry()
            .getService(EnversService.class);

        service.getAuditProcessManager().get(eventSource)
            .doBeforeTransactionCompletion(sessionImplementor);
    }
于 2020-05-19T10:38:17.913 回答
-1

其他两个解决方案对我不起作用,所以我使用了另一种方式,我只是创建一个新事务并强制提交。每次我需要一个新的修订版时,我都会再做一次。

@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")它可能会绕过提交并将每个测试视为一个事务(因此不会在同一个测试中多次版本控制......)

于 2016-03-30T08:56:32.477 回答