인프런 커뮤니티 질문&답변

정윤수님의 프로필 이미지
정윤수

작성한 질문수

호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)

게시글 조회 1 - 단건조회

Testcase 상에서 @Trasactional 사용시

작성

·

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();
    }
}

 

PostServiceTest.java

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를 찾을 수 없게됩니다.

image

해당 내용은 [스프링 공식 문서](https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testcontext-tx-rollback-and-commit-behavior)에 나와있습니다.

@Transactional이 아닌 @Rollback(false)를 추가하시면 윤수님이 의도한대로 작동할겁니다.

그런데

윤수님의 궁금증으로 테스트 하고 계신지 실제 이런 테스트 방식으로 진행 하실지는 잘 모르겠으나 몇 가지 덧 붙입니다.

이런식의 명시적이지 않은 의존 테스트는 좋은 방식이 아닙니다.

현재는 데이터저장 테스트 한 가지 메서드만 의존하고 있지만 앞으로 다른 테스트가 추가될때 이런 의존성이 높아질 위험이 있습니다. 향후에 윤수님이 아닌 다른 누군가가 나중에 데이터저장 테스트를 수정할때를 생각해보면 그 누구도 데이터조회 테스트를 고려하지 않을 겁니다.
또한 앞으로 추가될 테스트가 이 전에 저장 되어버린 데이터(Post=1)로 인하여 의도치않게 테스트 실패 또는 성공이 될 수 있습니다.

또한 데이터저장 테스트 > 데이터조회 테스트 와 같이 테스트 순서를 보장할 수 없습니다. 현재는 저장 후 조회하는 메서드가 실행되지만 항상 보장될 수 있는건 아닙니다. 앞으로 테스트 메서드가 추가됨에 따라 언제든지 달라질 수 있습니다.

각 테스트 수행 메서드는 명확하게 데이터가 정리된 상태에서 환경구성, 수행, 검증하는 것을 추천합니다.

감사합니다.

정윤수님의 프로필 이미지
정윤수
질문자

호돌맨님 감사합니다! 많은 것을 알려주시고, 구글링을 하는 법까지 간접적으로 알려주셔서 너무 감사합니다!!! 호돌맨님 최고십니다:)

정윤수님의 프로필 이미지
정윤수

작성한 질문수

질문하기