我正在尝试围绕某些审核实体进行一些测试。我的问题是只对事务提交进行审核。
我需要创建/编辑一些测试对象,提交事务,然后检查修订。
与envers进行集成测试的最佳方法是什么?
更新:这是我想要实现的非常糟糕的,不确定性的测试类。我宁愿不依赖测试方法的顺序来执行此操作
首先在单个交易中创建一个account和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);
}
我是Spring事务测试支持的用户,该事务在完成后会回滚测试,并且由于envers的设计,因此不会创建修订。我创建了一个hack,该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查询。
根据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;
}
});
}
}
[这是从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
}
}
然后在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);
然后您可以测试数据库的内容并最终回滚事务。
Envers @thomas-naskali's answer的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);
}
另外两个解决方案对我不起作用,所以我使用了另一种方法,我只是创建一个新事务并强制提交。每当我需要新的修订版本时,都会再次进行。
@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")
,它可能会绕过提交并将每个测试视为一个事务(因此,在同一测试中不会多次版本化...)