Cécile Fecherolle
Cécile Fecherolle

Reputation: 1754

Insert into test database is not executed in @DataJpaTest

I created a @DataJpaTest class to test a JpaRepository in my SpringBoot project, this test class is setup like so:

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureEmbeddedDatabase
public class MyRepositoryTest {

    @Autowired private TestEntityManager entityManager;

    @Autowired private MyRepository myRepository;

    private MyEntity testEntity;

    @Test
    public void shouldReturnAllEntities() {
        myEntity = new MyEntity();

        myRepository.save(myEntity);

        assertEquals(1, myRepository.findAll().size());
    }
}

@AutoConfigureEmbeddedDatabase is an annotation provided by a package for testing with an embedded PostgreSQL database, which is embedded-database-spring-test.

This test fails miserably, with a list size of 0 instead of the expected 1.

When executing this test, I see a transaction is being started by DataJpaTest at the beginning of test execution (which I expected) and then some SQL logs:

After failing, the transaction rolls back as expected from a DataJpaTest (as I can see from the logs).

I tried investigating this situation: my entity is sucessfully persisted in the EntityManager but the insert action is queued/not executed. When I add a flush call somewhere between the save and the findAll calls, test crashes because there is no ongoing transaction.

I have no clue how to "trigger" the actual insert into my test database, and thought it would occur automatically in such a simple setup.

Feel free to ask for more details, at this point I will take any hint I can get. Thanks in advance

Upvotes: 1

Views: 2651

Answers (2)

Cécile Fecherolle
Cécile Fecherolle

Reputation: 1754

After hours of rummaging through documentation and trying every possible combination of annotations in the test class, I finally figured out this is not actually a problem one can see in the code I posted (sorry guys...) but a very random side-effect of another annotation I'm using in my SpringBoot application : @EnableBatchProcessing

It occurred to me while reading this related issue: https://github.com/spring-projects/spring-boot/issues/10703

While this seems totally unrelated at first sight, this annotation (on the application class) changes the transactions handling in the app and gets picked up by my test class, among other stuff.

This annotation caused the errors I got when trying to flush after saving entities, saying there was no transaction in progress.

I first tried removing it from the main application class (monkey-testing, obviously) and saw that after this change, I could flush changes and see normal behaviour for my entityManager and repository in the test class.

Then, I figured out a solution to avoid breaking my Spring Batch production code but make my tests work. I moved the annotation in another class, annotated with @Configuration:

@Configuration
@EnableBatchProcessing
public class BatchConfig {
}

This class gets scanned by the application so there is no side effet when we start it. To avoid having it in my test setup, I created a new class:

public class DaoTestConfiguration {

    @MockBean BatchConfig batchConfig;
}

I found this quite elegant, but maybe there are better ways to ignore this annotation in specific classes. To use this new class, I added this annotation to my test class:

@Import(DaoTestConfiguration.class)

Works like a charm! I can now execute my tests as I wanted, saving and then flushing without errors and getting expected results in find methods.

I hope this can help others stumbling on this weird issue.

Upvotes: 3

Antoniossss
Antoniossss

Reputation: 32517

Here you have complete example that actually works

@RunWith(SpringRunner.class)
@DataJpaTest
@EnableJpaRepositories
@EntityScan
@SpringBootTest(classes = MyRepo.class)
public class SampleTest {
    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private MyRepo myRepo;

    private MyEntity testEntity;

    @Test
    public void shouldReturnAllEntities() {
        MyEntity myEntity = new MyEntity();
        myEntity.setId(100l);
        myRepo.save(myEntity);

        Assertions.assertThat(myRepo.findAll()).hasSize(1);
    }
}

@Repository
interface MyRepo extends JpaRepository<MyEntity, Long> {

}

@Entity
@Data
class MyEntity {
    @Id
    Long id;
}

What I recently did, to test underlying persistence logic is that I annotated test class with

@EnableJpaRepositories("mypackage")
@EntityScan("mypackage")
@SpringBootTest
@DataJpaTest

I don't know which of those can be ommited, but it works

Unfortunetly my integration tests involves db interaction via service (so its not 1:1 your test case), in in test method I have call to @Transactional annotated. Terefore I don't know if wil make difference if you annotate test method directly with @Transactional or not. Try out both scenarios, so there will be something to move on from.

Upvotes: 1

Related Questions