Reputation: 1754
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:
select nextval
of the myEntity table sequence, which gets executed two times (Why? No idea.)select
request issued by the call to findAll
insert
has been made.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
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
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