Spring Boot 测试中的事务未回滚

Posted

技术标签:

【中文标题】Spring Boot 测试中的事务未回滚【英文标题】:Transactions in spring boot testing not rolled back 【发布时间】:2018-03-25 14:03:58 【问题描述】:

我的UserController 有一个集成测试类。下面类的内容是:

// imports...

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
@Transactional
@Rollback
public class UserControllerTests 

    private static final String ENDPOINT = "/v1/users";

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private ApplicationProperties applicationProperties;

    @Test
    public void test_user_create() 
        String token = login("test", "test");
        HttpEntity<UserRequest> request = createRequest(token, "admin", "admin");
        ResponseEntity<User> response = restTemplate.exchange(ENDPOINT, HttpMethod.POST, request, User.class);

        assertEquals(HttpStatus.CREATED, response.getStatusCode());
    

    private HttpEntity createRequest(String token) 
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.set("Authorization", String.format("Bearer %s", token));
        return new HttpEntity(headers);
    

    private HttpEntity<UserRequest> createRequest(String token, String username, String password) 
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.set("Authorization", String.format("Bearer %s", token));
        return new HttpEntity<>(new UserRequest(username, password), headers);
    

    private String login(String username, String password) 
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.set("Authorization", String.format("Basic %s", Base64.getEncoder().encodeToString(String.format("%s:%s", applicationProperties.getAuth().getClientId(), applicationProperties.getAuth().getClientSecret()).getBytes())));
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "password");
        body.add("username", username);
        body.add("password", password);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
        ResponseEntity<OAuth2AccessToken> response = restTemplate.exchange("/oauth/token", HttpMethod.POST, request, OAuth2AccessToken.class);
        return response.getBody().getValue();
    

当我执行这个测试类两次时,第二次它失败了,因为数据库中已经有一个用户名为admin(唯一约束)的用户。

我正在测试一个与我的生产环境相同的postgres 数据库。该应用程序使用 Spring 的 jdbcTemplate 进行数据库操作。

我的日志产生了以下日志:

2017-10-13 14:11:31.407  INFO [iam-service,,,] 63566 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context 
...
2017-10-13 14:11:32.050  INFO [iam-service,,,] 63566 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test context 

我的应用流程是&lt;request&gt; --&gt; &lt;controller&gt; --&gt; &lt;service with jdbcTemplate&gt;,服务是用@Transactional注解的。

我真的被这个困住了。

找到的一个解决方案对我不起作用,它正在为测试配置创建一个 PlatformTransactionManager bean:

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) 
    return new DataSourceTransactionManager(dataSource);

【问题讨论】:

您的服务层的@Transactional 方法上是否有REQUIRES_NEW @Patrick 这不起作用 是的,这就是我问的原因。这可能是个问题。 @mmjmanders 你有这个项目在公共回购的某个地方发布,或者你能做一个孤立的例子,重复这个错误吗? @AlexSaunin 我创建了一个:github.com/mmjmanders/spring-boot-transactional-test 【参考方案1】:

根据官方Spring Boot documentationdb事务回滚不支持直接从“web层”应用:

如果您的测试是@Transactional,它将回滚事务 默认情况下每个测试方法的结束。然而,作为使用这个 与RANDOM_PORTDEFINED_PORT 隐式安排 提供真实的servlet环境,HTTP客户端和服务器将运行 在单独的线程中,因此是单独的事务。任何交易 在这种情况下,在服务器上发起的不会回滚。

我建议您考虑以下选项:

在单元测试的情况下,对web controller 层和database 层使用单独的测试

在执行集成测试时,在执行测试方法之前创建/恢复表并在执行测试方法之后删除/清除它们。当 Db 架构很大时,这种方法可能会产生很大的开销,但您可以根据需要有选择地清除/恢复数据。

【讨论】:

有没有办法在彼此之间共享事务上下文?

以上是关于Spring Boot 测试中的事务未回滚的主要内容,如果未能解决你的问题,请参考以下文章

嵌套事务总结

嵌套事务总结

Spring Boot 事物回滚

Spring 事务提交回滚源码解析

Spring 事务提交回滚源码解析

Spring Boot 事物回滚