공식팀의 테스트코드 최적화 with 테스트 격리
현재 저희 공식팀은 인수 테스트와 @WebMvc를 통한 컨트롤러 테스트, @SpringBootTest를 활용한 서비스 테스트, 그리고 @DataJpaRepository를 활용한 레포지토리 테스트로 크게 4종류의 테스트를 진행 중입니다.
테스트 격리
테스트 격리의 방식으로는 다양한 방식이 있습니다.
- DirtiesContext 사용하기
- 가장 쉬운 방법이지만 매번 컨텍스트를 올려야 하므로 느린 방식이어서 저희 팀은 처음부터 해당 방식을 적용하지 않았습니다.
- @Sql을 활용한 sql문 미리 작성하기
- 해당 방법을 미션 하면서 적용해보았다. 그때는 규모도 작고 페어로 진행하기 때문에 크게 문제가 되지 않았다.
- 그런데 팀 프로젝트에 적용하기엔 아래와 같은 단점이 많았다. 따라서 이것도 적용하지 못했다.
- 테스트에 필요한 더미 데이터가 무엇인지 확인하려면 매번 sql파일을 봐야 해서 번거롭다.
- 테이블이 추가되거나 칼럼이 변경되는 등 매번 sql문과의 싱크를 확인해줘야 하는 불편함이 있다
- @Transactional을 이용한 격리
- 대부분 사용하는 경우일 것이다. 하지만 인수 테스트에서는 HTTP요청을 보내는 client와 서버가 각각 다른 스레드에서 실행되므로 무의미하다.
- 인수 테스트 이외의 곳에서는 다음과 같은 이유로 사용하지 않고 있다. >> https://giron.tistory.com/133/
- truncate를 활용한 DataBaseCleaner설정
- @Transactional 사용하지 못해서 결정한 방법이다. 해당 방식은 자동으로 테스트가 끝날 때마다 truncate를 통해 롤백시켜준다. -> 현재 저희 공식팀에서 채택한 방법이다.
진짜 최적화 이야기
앞의 서론을 말한 이유는 이미 저희 팀은 더티컨텍스트를 사용하지 않아서 나쁘지 않은 테스트 속도를 보이고 있습니다. 노트북 사양마다 다르지만 365개 테스트 기준으로 30초 안팎을 기록하고 있습니다.
하지만 체감상으로는 여전히 느리다고 느꼈습니다. 실제 젠킨스에서는 Build and Test 가 2분이 걸리는 것을 확인할 수 있습니다.
더티 컨텍스트는 이미 제거되었고 다른 테스트 속도를 줄이는 방법이 무엇이 있을까 고민을 했고 찾은 방법으로는 컨텍스트 캐싱을 활용하자는 결론이 나왔습니다.
테스트 컨텍스트 프레임워크
개선
현재 테스트마다 컨텍스트가 뜨는 부분은 크게 2 부분이었습니다.
- WebMvc테스트는 전부 다 뜨고
- AuthService만 다른 service테스트에 없는 mock이 있어서 다시 뜬다.
따라서 WebMvc테스트에서 전부 같은 빈을 띄워주도록 아래처럼 변경을 했다. Service Test도 마찬가지로 빈들을 통합시켜주었다.
@AutoConfigureRestDocs
@WebMvcTest({
MemberController.class,
ArticleController.class,
TempArticleController.class,
AuthController.class,
TagController.class,
CommentController.class,
LikeController.class,
VoteController.class
})
public abstract class ControllerTest {
@MockBean
protected ArticleService articleService;
@MockBean
protected JwtTokenProvider jwtTokenProvider;
@MockBean
protected TempArticleService tempArticleService;
@MockBean
protected CommentService commentService;
@MockBean
protected LikeService likeService;
@MockBean
protected MemberService memberService;
@MockBean
protected TagService tagService;
@MockBean
protected VoteService voteService;
@MockBean
protected AuthService authService;
@Autowired
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper;
}
이렇게 빈들을 통합해서 결론적으로는 총 4번만 컨텍스트가 띄우도록 했다. 인수 테스트, 컨트롤러 테스트, 서비스 테스트 그리고 레포지토리 테스트 총 4번만 컨텍스트가 띄워진다. 아래의 사진을 보면 체감을 느낄 수 있었다.
평균 테스트 빌드 시간과 비교해보면 평균 8초가량이 줄어든 것을 알 수 있다!!!!!
참고로 컨텍스트 캐싱을 통한 방식은 로컬에서 테스트 시간으론 측정이 안된다. 테스트 측정 시간은 컨텍스트가 뜬 후에 돌아가므로 로컬에서는 시각적으로 확인할 방법이 없다.
Reference