【问题标题】:Spring Boot integration test fails if @Transactional如果@Transactional,Spring Boot 集成测试失败
【发布时间】:2021-03-20 17:39:59
【问题描述】:

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

@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 与完整的集成测试一起使用是行不通的(在使用MockMvc 时它会起作用,因为它不会发出实际请求并在同一个线程和事务中运行。
  • 确实有道理。那么在每次集成测试之后/之前重置数据库状态的通常做法是什么?只是删除@AfterEach 方法中的所有内容?您是否考虑将您的评论转换为答案,以便我接受并提供给其他人?

标签: spring spring-boot spring-data spring-boot-test spring-data-jdbc


【解决方案1】:

@Transactional 与系统测试一起使用是行不通的。

发生的情况是您的@BeforeEach 方法中的数据被插入到数据库中事务永远不会提交。数据仅在同一事务中可见。

现在您向服务器发送一个 HTTP 请求,该请求打开一个与数据库的新连接并启动一个新事务。但是,由于其他事务中的数据未提交,因此它什么也看不到。

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

要为每个测试使用数据设置,但在进行完整系统测试时应谨慎使用!你要么:

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

【讨论】:

    猜你喜欢
    • 2019-12-26
    • 2017-07-02
    • 2020-03-18
    • 1970-01-01
    • 2013-08-25
    • 2016-12-30
    • 2020-02-25
    • 1970-01-01
    • 2021-07-21
    相关资源
    最近更新 更多