Spring 如果@Transactional,引导集成测试失败

Spring Boot integration test fails if @Transactional

我有一个简单的 Spring 启动应用程序,它使用 Spring 数据 JDBC 存储库并公开 REST API。除了我的集成测试,一切似乎都 运行 OK。请参阅下面的存储库配置摘录。

@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)
    }
}

集成测试如下。

@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"))
    }
}

如果不应用@Transactional注释,则测试成功。因为我需要在每次测试后回滚更改,所以我想让测试成为事务性的。问题是,在那种情况下,响应不包含任何项目,比如数据库将是空的,我不知道为什么。

使用 @Transactional 进行系统测试将不起作用。

您的 @BeforeEach 方法中的数据被插入到数据库中 但是 事务从未提交。数据仅在同一事务内可见。

现在您向服务器发送一个 HTTP 请求,服务器打开一个新的数据库连接并开始一个新的事务。但是,由于未提交来自其他事务的数据,因此它不会看到任何内容。

@Transactionl 仅在使用 MockMvc 时有效,因为这将 运行 在同一事务和线程中。实际发出的 HTTP 请求并非如此。

对每个测试都使用数据设置,尽管在进行全系统测试时应谨慎使用!你要么:

  1. 如果您真的想要系统测试,请以无所谓的方式编写测试。
  2. 使用 MockMvc 来 运行 您的测试,而不是实际的 HTTP 请求,这将使其(重新)使用相同的事务。
  3. 不要编写系统测试,而是使用 @WebMvcTest 来测试您的控制器