Reputation: 8200
I have a simple Spring Boot application using Spring Data JDBC repositories and exposing REST API. Everything seems to be running OK except for my integration tests. See below an excerpt from repository configuration.
@Configuration
@EnableJdbcRepositories
class RepositoryConfig : AbstractJdbcConfiguration() {
@Bean
@Profile("int-test")
fun intTestDataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("sql/h2/schema_create.sql")
.build()
}
@Bean
fun namedParameterJdbcOperations(dataSource: DataSource): NamedParameterJdbcOperations {
return NamedParameterJdbcTemplate(dataSource)
}
@Bean
fun transactionManager(dataSource: DataSource): TransactionManager {
return DataSourceTransactionManager(dataSource)
}
}
The integration test follows.
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ActiveProfiles("int-test")
@Transactional
class OrganizationControllerIntTest {
@Autowired
private lateinit var organizationRepository: OrganizationRepository
@Autowired
private lateinit var restTemplate: TestRestTemplate
@Autowired
private lateinit var testDataGenerator: TestDataGenerator
@BeforeEach
fun beforeEach() {
val organizations = testDataGenerator.organizations(10).toList()
organizationRepository.saveAll(organizations)
}
@Test
fun `When GET organizations then return all organizations`() {
val result = restTemplate.getForEntity("/organizations", String::class.java)
assertThat(result?.statusCode, equalTo(HttpStatus.OK))
assertThat(result?.body, containsString("items"))
}
}
The test is successful if @Transactional
annotation is not applied. Since I need the changes to be rollbacked after each test, I would like to make the tests transactional. The problem is that in that case the response does not contain any items like the database would be empty and I cannot figure out why.
Upvotes: 2
Views: 5797
Reputation: 124441
Using @Transactional
with a system test won't work.
What happens is the data in your @BeforeEach
method is inserted into the database but the transaction never commits. The data is only visible inside the same transaction.
Now you send a HTTP request to the server which opens a new connection to the database and starts a new transaction. However as the data from the other transaction isn't committed it won't see anything.
The @Transactionl
will only work when using MockMvc
as that will run inside the same transaction and thread. With an actual HTTP request being made that isn't the case.
To use data setup for each test, although you should be cautious with this when doing a full system test! you either:
MockMvc
to run your test, instead of an actual HTTP request which will make it (re)use the same transaction.@WebMvcTest
to test your controllerUpvotes: 6