I have a Spring service that does something like that :
@Service
public class MyService {
@Transactional(propagation = Propagation.NEVER)
public void doStuff(UUID id) {
// call an external service, via http for example, can be long
// update the database, with a transactionTemplate for example
}
}
The Propagation.NEVER indicates we must not have an active transaction when the method is called because we don't want to block a connection to the database while waiting for an answer from the external service.
Now, how could I properly test this and then rollback the database ? @Transactional on the test won't work, there will be an exception because of Propagation.NEVER.
@SpringBootTest
@Transactional
public class MyServiceTest {
@Autowired
private MyService myService;
public void testDoStuff() {
putMyTestDataInDb();
myService.doStuff(); // <- fails no transaction should be active
assertThat(myData).isTheWayIExpectedItToBe();
}
}
I can remove the @Transactional but then my database is not in a consistent state for the next test.
For now my solution is to truncate all tables of my database after each test in a @AfterEach junit callback, but this is a bit clunky and gets quite slow when the database has more than a few tables.
Here comes my question : how could I rollback the changes done to my database without truncating/using @Transactional ?
The database I'm testing against is mariadb with testcontainers, so a solution that would work only with mariadb/mysql would be enough for me. But something more general would be great !
(another exemple where I would like to be able to not use @Transactional on the test : sometimes I want to test that transaction boundaries are correctly put in the code, and not hit some lazy loading exceptions at runtime because I forgot a @Transactional somewhere in the production code).
Some other precisions, if that helps :
- I use JPA with Hibernate
- The database is create with liquibase when the application context starts
Others ideas I've played with :
- @DirtiesContext : this is a lot slower, creating a new context is a lot more expensive than just truncating all tables in my database
- MariaDB SAVEPOINT : dead end, it's just a way to go back to a state of the database INSIDE a transaction. This would be the ideal solution IMO if i could work globally
- Trying to fiddle with connections, issuing
START TRANSACTIONstatements natively on the datasource before the test andROLLBACKafter the tests : really dirty, could not make it work