JdbcTestUtils和事务上下文测试



我遇到这个问题,导致我在使用事务上下文运行测试时不使用JdbcTestUtils

如果我用@Transactional注释包装我的测试并使用JdbcTemplate/DataSource,那么生产代码中使用的事务和JdbcTestUtils使用的事务看起来不一样,所以如果我在then部分中查询数据库,断言就会失败。

这是一个伪测试:

@SpringBootTest
class TestClass {
@Autowired
private WebApplicationContext context;
@BeforeEach
void setup() {
RestAssuredMockMvc.webAppContextSetup(context, springSecurity());
}
@Test
@Transactional
@DisplayName("test1")
void test1(@Autowired DataSource dataSource) {
//given
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
assertThat(countRowsInTable(jdbcTemplate, "some_tbl")).isEqualTo(1);
//when
// Execute app code here that adds a record to some_tbl
//then
assertThat(countRowsInTable(jdbcTemplate, "some_tbl")).isEqualTo(2); //<- Assertion fails. 
}

作为一种变通方法,我需要使用Spring上下文测试存储库来检索测试中的数据,但这感觉是个坏主意,我需要维护这些存储库。

下面你会发现一个简单的spring-boot项目来展示这个问题。https://github.com/Marek00Malik/JdbcTestUtils-sample

我克隆了您的项目,可以在我的机器上验证失败的测试。

测试failingTestStoringNewObject:断言中测试失败的原因

assertThat(countRowsInTable(jdbcTemplate, "sample_table")).isEqualTo(1);

JPA的EntityManger(Spring Data JPA存储库在后台使用它(遵循写后处理策略,并将对JPA实体的更改保存在其一级缓存(内存中(中。当代码使用EntityManager执行.save()或任何其他数据库操作时,并不总是刷新对数据库的更改。

EntityManger试图推迟刷新,从而尽可能晚地将其一级缓存与数据库同步。关于这一点有很多值得阅读的地方,我可以推荐Vlad Mihalcea的指南。

在您的情况下,当您用@Transactional注释测试时,您的更改将在测试后回滚,因此永远不会提交到数据库。你可以在测试日志中看到这一点:

2020-07-20 09:59:57.754  INFO 12648 --- [    Test worker] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@2b8ade2d testClass = SimpleObjectResourceHttpTest, testInstance = pl.code.house.example.jdbc.template.jdbctemplatetest.SimpleObjectResourceHttpTest@

为了对此进行更多的可视化,您还可以使用为测试启用SQL日志记录

spring:
jpa:
show-sql: on

您将看到实际上没有执行INSERT语句,而是调用以获取实体的主键。

如果暂时SimpleObjectFacade中使用repository.saveAndFlush(newObj).toDto();,您会发现它是有效的。

我会使用SimpleObjectRepository进行集成测试,并使用它的.count()方法。在这种情况下,底层EntityManager识别对同一个表的调用,并在此之前刷新其当前更改,您将得到预期的结果。因为如果使用JdbcTemplate,则不会与EntityManager交互,因此EntityManager不会在使用SELECT COUNT(0) ...直接访问数据库时刷新它的更改。

更新:如果您不想进行任何调整,并且仍然将JdbcTemplate与Hibernate和JpaRepositories结合使用,则可以仅为测试设置以下刷新模式:

spring.jpa.properties.org.hibernate.flushMode=ALWAYS

这确保了始终刷新持久性上下文

最新更新