I am using Spring Boot 2 with JUnit 5, writing a DataJpaTest for a JpaRepository. The tests are run against a real PostgreSQL database (not embedded, not in-memory).
According to the documentation, by default each test is in a transaction and gets rolled back automatically. Indeed, that is the behavior I get without doing any extra effort. This is what my test looks like initially:
@ExtendWith(SpringExtension.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@DataJpaTest
class MyRepositoryIT {
@Autowired
private TestEntityManager entityManager;
@Autowired
private MyRepository myRepository;
@Test
void save() {
// arrange
MyEntity entity = new MyEntity().withName("Nikolaos");
// act
MyEntity saved = myRepository.save(entity);
// assert
assertThat(saved.getId()).isGreaterThan(0);
}
This works as expected, in fact in the test output I can see that it is rolling back the transaction:
20180822T085911 main INFO c.u.m.a.d.MyRepositoryIT Started MyRepositoryIT in 8.352 seconds (JVM running for 10.726)
20180822T085911 main INFO o.s.t.c.t.TransactionContext Began transaction (1) for test context [DefaultTestContext@1a8e9ed9 testClass = MyRepositoryIT, testInstance = acme.auth.db.MyRepositoryIT@68ea253b, testMethod = save@MyRepositoryIT, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@12fcb2c3 testClass = MyRepositoryIT, locations = '{}', classes = '{class acme.auth.Swagger2SpringBoot}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@57bd6a8f key = [org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@57d5872c, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6ee12bac, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@1b083826, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@3dc05093, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@2d9d4f9d], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@6a6c7f42]; rollback [true]
Hibernate: insert into MyTable (firstname) values (?)
20180822T085912 main INFO o.h.h.i.QueryTranslatorFactoryInitiator HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select My0_.id as id1_4_, My0_.firstname as firstname2_4_ from My My0_
20180822T085912 main INFO o.s.t.c.t.TransactionContext Rolled back transaction for test: [DefaultTestContext@1a8e9ed9 testClass = MyRepositoryIT, testInstance = acme.auth.db.MyRepositoryIT@68ea253b, testMethod = save@MyRepositoryIT, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@12fcb2c3 testClass = MyRepositoryIT, locations = '{}', classes = '{class acme.auth.Swagger2SpringBoot}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@57bd6a8f key = [org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@57d5872c, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6ee12bac, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@1b083826, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@3dc05093, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@2d9d4f9d], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]
and indeed in the database the table does not have any leftover records from the test.
However, I want to use JUnit 5 Nested tests, so that I can structure my test preconditions.
When I change my test like this, it no longer rolls back the transaction and the database is polluted:
@ExtendWith(SpringExtension.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@DataJpaTest
class MyRepositoryIT {
@Autowired
private TestEntityManager entityManager;
@Autowired
private MyRepository myRepository;
@Nested
class WhenTableIsEmpty {
@Test
void save() {
// arrange
MyEntity entity = new MyEntity().withName("Nikolaos");
// act
MyEntity saved = myRepository.save(entity);
// assert
assertThat(saved.getId()).isGreaterThan(0);
}
}
The test also passes, but it leaves the created record in the database. In the test output, there is no mention whatsoever of the word transaction, so it seems the transaction mechanism I get for free in the first un-nested test does not kick in in the nested case.
Does anyone have experience with this scenario?