Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Hibernate Envers

I would like to write tests about revision. In the console I see the update call of Hibernate, BUT no insertions into AUD-Table.

Test-Method:

@DataJpaTest
class JPAHistoryTest {

    @Test
    public void test() {
        def entity = // create Entity
        def entity2 = // create same Entity
        entity2.setPrice(entity2.getPrice() + 10)
        entity2.setLastUpdate(entity2.lastUpdate.plusSeconds(10))

        service.save(entity)
        service.save(entity2)
        repository.flush() // Hibernate updates changes

        assert repository.findRevisions(entity.id).content.empty == false // FAIL!
    }
}

My Entity looks like:

@Entity
@Audited
class Entity {
    @Id @GeneratedValue Long id
    @Column(nullable = false) BigDecimal price
}

Thank you very much.

like image 231
Michael Hegner Avatar asked Jan 20 '18 18:01

Michael Hegner


People also ask

What is hibernate Envers?

The Envers module is a core Hibernate model that works both with Hibernate and JPA. In fact, you can use Envers anywhere Hibernate works whether that is standalone, inside WildFly or JBoss AS, Spring, Grails, etc. The Envers module aims to provide an easy auditing / versioning solution for entity classes.

What is Revinfo?

Additionaly, there is a "REVINFO" table generated, which contains only two fields: the revision id and revision timestamp. A row is inserted into this table on each new revision, that is, on each commit of a transaction, which changes audited data.

What is @NotAudited?

Note that if Hotel was audited RelationTargetAuditMode.NOT_AUDITED will just be ignored (you will have historical data for Hotel). @ NotAudited means "I just don't care for this field in historical data" (it won't be saved, relationship will be null, you won't see it when looking historical data about CustomerBooking )


2 Answers

As I found out I keep the @DataJpaTest and add @Transactional(propagation = NOT_SUPPORTED) to make sure, the test methods will not start a transaction. Because if they would run in a transaction, then the envers history entries will be written, when the test close the transaction.

@RunWith(SpringRunner)
@DataJpaTest
@Transactional(propagation = NOT_SUPPORTED)
class JPAHistoryTest {
    @After
    void after() {
        repository.deleteAll()
    }

    @Test
    public void testTwoInsert() {
        def entity1 = // ...
        def entity2 = // ...

        sut.save(entity1 )
        sut.save(entity2 )

        assert repository.findRevisions(entity1.id).content.size() == 1
        assert repository.findRevisions(entity2.id).content.size() == 1
    }
}
like image 166
Michael Hegner Avatar answered Sep 30 '22 09:09

Michael Hegner


I found same problem as you and tried @michael-hegner answer, but I used TestEntityManager class so I can't get EntityManager when there is no transaction (propagation set to NOT_SUPPORTED).

In my case the solution was to manually commit transaction from TestEntityManager, to first save entity and changes and next query for revisions.

Here's a test class:

@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private TestEntityManager testEntityManager;

    @Test
    public void shouldBeAudited() {
        User user = getTestUser();
        testEntityManager.persistAndFlush(user);

        user.setPassword("tset");
        testEntityManager.merge(user);

        // This line is critical here to commit transaction and trigger audit logs
        testEntityManager.getEntityManager().getTransaction().commit();

        // With propagation = NOT_SUPPORTED this doesn't work: testEntityManager.getEntityManager()
        AuditReader auditReader = AuditReaderFactory.get(testEntityManager.getEntityManager());
        List revisions = auditReader.createQuery()
                .forRevisionsOfEntity(User.class, true)
                .add(AuditEntity.id().eq(user.getId()))
                .getResultList();

        assertEquals(1, revisions.size());
    }

    private User getTestUser() {
        User user = new User();
        user.setUsername("test");
        user.setEmail("test");
        user.setPassword("test");
        return user;
    }

}

It could be mandatory to manually remove user after Test because transaction was commited and in other tests it may causes some problems.

like image 45
Michał Kupisiński Avatar answered Sep 30 '22 09:09

Michał Kupisiński