spring - spring boot 测试中的事务未回滚

标签 spring postgresql spring-boot junit spring-jdbc

我的 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 

我的申请流程是<request> --> <controller> --> <service with jdbcTemplate>服务用 @Transactional 注释.

我真的被这个困住了。

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

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

最佳答案

据官方Spring Boot documentation当您直接从“web 层”应用它时,不支持数据库事务回滚:

If your test is @Transactional, it will rollback the transaction at the end of each test method by default. However, as using this arrangement with either RANDOM_PORT or DEFINED_PORT implicitly provides a real servlet environment, HTTP client and server will run in separate threads, thus separate transactions. Any transaction initiated on the server won’t rollback in this case.

我建议您考虑以下选项:

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

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

关于spring - spring boot 测试中的事务未回滚,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46729849/

相关文章:

java - 将 RestTemplate 与 SSL 一起使用

java - Spring /DI : Constructor injection in child class

java - 实体在 hibernate 中产生 classNotFound 异常

没有尾随零的 postgresql 数字类型

java - 如何在 Java 8 中进行条件方法链接

java - 在 Spring Boot 中,通过扩展 MappingJackson2HttpMessageConverter 添加自定义转换器似乎会覆盖现有转换器

java - 使用 Java8 过滤两个列表

postgresql - 如何在ODOO中查看保存的密码?

ruby-on-rails - 迁移时出现以下错误

java - Spring Controller 针对 404 状态和 url 模式的异常处理