After updating from 2.2.9 to 3.3.2, @Transactional nested tests do not roll back

The below two test methods create entities with the same id. In 2.2.9, the nested test method would trigger a rollback; in 2.3.2 it does not. (I'm attaching the full source. The 2.2.9 version is commented out in the pom to make it easy to switch between)

@SpringBootTest // Keep this at the top level so that we can inject common things into nested tests
class NestedTestsApplicationTests {

    @Autowired
    private FooEntityRepository repository;

    @Nested
    @SpringBootTest("foo.bar=foo") // Ensures unique context
    @Transactional
    class NestedTest {

        @Test
        public void testTransaction() {
            repository.saveAndFlush(new FooEntity("Foo")); // Save entity with same id as NestedTest2
        }
    }

    @Nested
    @SpringBootTest("foo.bar=bar") // Ensures unique context 
    @Transactional
    class NestedTest2 {

        @Test
        public void testTransaction() {
            repository.saveAndFlush(new FooEntity("Foo")); // Save entity with same id as NestedTest
        }
    }
}

I've tried downgrading spring-framework and spring-data and have seen no improvement.

I'm not sure if it's related or not but I'm also having heap space issues when running tests between these two versions. I'll log a separate issue if I can get more details on that.

nested-tests.zip

Comment From: snicoll

Thanks for the report and the sample.

in 2.3.2 it does not.

I am not entirely sure this is accurate. I can certainly see a log that states the transaction rollback has been initiated. I've managed to make your tests work with something that's a bit odd. In Spring Boot 2.3 we've switched to using unique databases per context. Adding spring.datasource.generate-unique-name=false to your project with 2.3.2.RELEASE fixes the issue. It sounds like those tests can't work if each work on its own database.

Comment From: jlfeld1

@snicoll . This makes a lot of sense now. I didn't see it documented in the release notes anywhere. Thanks for your help.

Comment From: sbrannen

java @SpringBootTest // Keep this at the top level so that we can inject common things into nested tests class NestedTestsApplicationTests {

Please note that this is not a recommended practice.

  1. You end up with multiple (perhaps identical) copies of your application (i.e., ApplicationContext) loaded in memory which can degrade your build in terms of time and memory.
  2. The FooEntityRepository that is used in the nested tests is in no way associated with the other beans in the ApplicationContext loaded for the nested test class. That's analogous to using components from different applications running in different processes and expecting them to be able to be able to communicate, share state, etc.

Keeping that in mind, an alternative way to make your tests pass is the following. Note that the FooEntityRepository is autowired from the ApplicationContext for the @Nested test class in which it is used. In addition, there is no ApplicationContext loaded for NestedTestsApplicationTests.

class NestedTestsApplicationTests {

    @Nested
    @SpringBootTest("foo.bar=foo") // Ensures unique context
    @Transactional
    class NestedTest {

        @Autowired
        private FooEntityRepository repository;

        @Test
        public void testTransaction() {
            repository.saveAndFlush(new FooEntity("Foo")); // Save entity with same id as NestedTest2
        }
    }

    @Nested
    @SpringBootTest("foo.bar=bar") // Ensures unique context 
    @Transactional
    class NestedTest2 {

        @Autowired
        private FooEntityRepository repository;

        @Test
        public void testTransaction() {
            repository.saveAndFlush(new FooEntity("Foo")); // Save entity with same id as NestedTest
        }
    }
}

Having said that, it's not clear why you need to "Ensure [a] unique context" for your sibling nested test classes. One would typically avoid that if possible, and if it's absolutely necessary that the contexts not be reused, one might consider using @DirtiesContext.

Comment From: jlfeld1

@sbrannen Thanks for the response. I agree completely with your assessment. This approach of moving the injections down into the nested class is the approach I took to address my problem before I logged this issue. The comment about // Ensures unique context was just to demonstrate that there are slight configuration differences between the two and not to prevent cached context reuse.

I do think the new approach is better, we just have to adjust the way we are writing some of our tests.