2017-10-13 1 views
6

통합 테스트 클래스가 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와 주석 있습니다

내 기록은 다음과 같은 로그를 생산했다.

저는 정말 이걸 고집합니다. 당신은에서 직접 적용 할 때 지원되지 않습니다

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

서비스 레이어의'@ Transactional' 메소드에'REQUIRES_NEW'가 있습니까? – Patrick

+0

@ 패트릭이 작동하지 않습니다 – mmjmanders

+0

그래, 그게 왜 내가 물어 보는거야. 이것은 문제가 될 수 있습니다. – Patrick

답변

4

공식 Spring Boot documentation DB 트랜잭션 롤백에 따르면

한 가지 해결책은,이 테스트 구성에 대한 PlatformTransactionManager 빈을 생성하고 나를 위해 작동하지 않았다 발견 "웹 계층"테스트가 @Transactional입니다

경우가 기본적으로 각 시험 방법의 끝에서 트랜잭션을 롤백합니다. 그러나이 배열을 사용하면 RANDOM_PORT 또는 DEFINED_PORT이 암시 적으로 이 실제 서블릿 환경을 제공하므로 HTTP 클라이언트와 서버는 별도의 스레드에서 을 실행하므로 트랜잭션이 분리됩니다. 이 경우 서버에서 시작된 트랜잭션은 롤백되지 않습니다. web controller 층 및 단위 테스트의 경우 database 계층

  • 이 생성/& 드롭/지우기 전에 테이블을 복원하기위한

    • 를 사용하여 별도의 시험 :

  • 나는 다음과 같은 옵션을 고려 제안 통합 테스트가 수행 될 때 테스트 메소드가 실행 된 후 이 접근 방식은 Db 스키마가 클 때 상당한 오버 헤드가있을 수 있지만 사용자의 요구에 따라 선택적으로 데이터를 지우거나 복원 할 수 있습니다.

    관련 문제