작성
·
609
3
당연히 구글링 해보셨져? 원하는 결과를 못찾으셨나요? 어떤 검색어를 입력했는지 알려주세
문제가 발생한 코드(프로젝트)를 Github에 올리시고 링크를 알려주세요.
안녕하세요. 호돌맨님 강의 잘 보고 있습니다! Post 관련 테스트를 작성할 때, 저는 @Transactional을 이용해서 DB에 데이터가 반영되지 않도록 시도했습니다.그리고 테스트를 진행하였습니다. 메서드를 각각 테스틀 할 때는 통과했지만, 메서드 모두 동시에 돌릴 때는 테스트 통과에 실패하였습니다. @Transcational, 영속성 컨텍스트로 구글링을 시도했었습니다. @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)를 사용하면 영속성 컨텍스트를 새로 생성함으로써 테스트가 모두 통과가 되었지만, 매번 영속성 컨텍스트를 새로 생성하는 것은 비효율적이라고 생각했습니다.. 결론적으로 제가 본 바로는 유의미한 결과를 얻을 수 없었습니다.. 아래 코드와 그에 출력되는 결과를 이미지로 첨부해두었습니다. 혹시 어떤 것이 원인인건지 힌트라도 알려주실 수 있을까요?? 구글링 키워드를 알려주셨으면 좋겠습니다..! (해결법은 제가 찾겠습니다!!)
PostService.java
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class PostService {
private final PostRepository postRepository;
@Transactional
public void save(PostCreateDto postCreate){
// Post post = new Post(postCreate.getTitle(), postCreate.getContent());
Post post = Post.builder()
.title(postCreate.getTitle())
.content(postCreate.getContent())
.build();
postRepository.save(post);
}
public Long findPostById(Long postId){
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게시물입니다."));
return post.getId();
}
}
import com.toktok.core.domain.post.Post;
...
@SpringBootTest
@Transactional. //똑같이 영속성 컨텍스트를 공유하고 있기 때문에 save가 롤백되어야 하지 않나요??..
class PostServiceTest {
@Autowired
private PostService postService;
@Autowired
private PostRepository postRepository;
@Test
@DisplayName("데이터가 저장 되어야합니다.")
void save_test(){
//given
PostCreateDto postDto = PostCreateDto.builder()
.title("제목입니다.")
.content("내용입니다.")
.build();
//when
postService.save(postDto);
//then
Post post = postRepository.findAll().get(0);
assertThat(post.getId()).isEqualTo(1L);
assertThat(post.getTitle()).isEqualTo(postDto.getTitle());
}
@Test
@DisplayName("데이터는 하나 조회")
void find_test(){
//given
Long postId = 1L;
PostCreateDto postDto = PostCreateDto.builder()
.title("글 제목입니다.")
.content("글 내용입니다.")
.build();
postService.save(postDto);
//when
Long postById = postService.findPostById(1L);
//then
assertThat(postById).isEqualTo(1L);
}
@Test
@DisplayName("존재하지 않는 데이터 확인")
void not_exist_post(){
//given
Long notExistPostId = 2L;
Post post = Post.builder()
.title("글 제목입니다.")
.content("글 내용입니다.")
.build();
Post savedPost = postRepository.save(post);
//when
postService.findPostById(savedPost.getId());
//then
assertThrows(IllegalArgumentException.class, ()-> postService.findPostById(notExistPostId));
}
}
답변 1
1
안녕하세요. 호돌맨입니다.
흥미로운 테스트 방법 공유 감사합니다.
@Transactional이 걸려도 테스트 메서드가 수행되면 기본적으로 롤백이 됩니다. 따라서 두 번째 테스트 데이터는 하나 조회
메서드에서 이 전에 저장된 ID 값인 1L의 Post를 찾을 수 없게됩니다.
해당 내용은 [스프링 공식 문서](https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testcontext-tx-rollback-and-commit-behavior)에 나와있습니다.
@Transactional
이 아닌 @Rollback(false)
를 추가하시면 윤수님이 의도한대로 작동할겁니다.
윤수님의 궁금증으로 테스트 하고 계신지 실제 이런 테스트 방식으로 진행 하실지는 잘 모르겠으나 몇 가지 덧 붙입니다.
이런식의 명시적이지 않은 의존 테스트는 좋은 방식이 아닙니다.
현재는 데이터저장 테스트
한 가지 메서드만 의존하고 있지만 앞으로 다른 테스트가 추가될때 이런 의존성이 높아질 위험이 있습니다. 향후에 윤수님이 아닌 다른 누군가가 나중에 데이터저장 테스트
를 수정할때를 생각해보면 그 누구도 데이터조회 테스트
를 고려하지 않을 겁니다.
또한 앞으로 추가될 테스트가 이 전에 저장 되어버린 데이터(Post=1)로 인하여 의도치않게 테스트 실패 또는 성공이 될 수 있습니다.
또한 데이터저장 테스트
> 데이터조회 테스트
와 같이 테스트 순서를 보장할 수 없습니다. 현재는 저장 후 조회하는 메서드가 실행되지만 항상 보장될 수 있는건 아닙니다. 앞으로 테스트 메서드가 추가됨에 따라 언제든지 달라질 수 있습니다.
각 테스트 수행 메서드는 명확하게 데이터가 정리된 상태에서 환경구성, 수행, 검증하는 것을 추천합니다.
감사합니다.
호돌맨님 감사합니다! 많은 것을 알려주시고, 구글링을 하는 법까지 간접적으로 알려주셔서 너무 감사합니다!!! 호돌맨님 최고십니다:)