• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

테스트 컨텍스트에 대한 질문입니다.

22.08.27 00:20 작성 조회수 695

4

안녕하세요. 강의를 듣던중 테스트와 관련해서 한가지 궁금한 점이 있어서 질문 남깁니다.

일단 저는 현업에서 @SpringBootTest 를 사용하지 않고 서비스 계층은 목킹을 해서 별도의 스프링 컨텍스트를 사용하지 않고 테스트를 하고 있습니다. 문제는 컨트롤러 계층을 테스트 할 때인데요. @WebMvcTest 로 테스트를 할 때 하나의 컨트롤러를 테스트 할때는 상관이 없지만 통합테스트 형태로 모든 테스트를 실행시에는 @WebMvcTest 가 각각 달린 컨트롤러 테스트마다 별도의 스프링 컨텍스트가 뜨기 때문에 테스트가 느려지는데요. (모든 컨트롤러 테스트가 같은 빈 조합을 사용한다면 같은 컨텍스트를 사용하겠지만 그런 경우는 거의 없기 때문에 각각의 테스트 클래스마다 대부분 스프링 컨텍스트가 새로 뜨는거 같습니다.)

그래서 하나의 추상 컨트롤러 테스트에만 @WebMvcTest 를 달고 여기에 모든 테스트 대상 컨트롤러를 다 추가하고 이 추상 클래스를 상속받아 각각의 테스트 클래스를 사용하고 있습니다. 그래서 전체 테스트 시에는 테스트 속도가 빠라졌지만, 이렇게 하다보니 하나의 컨트롤러 테스트를 할 때도 상속받은 추상클래스에 있는 모든 테스트 컨트롤러가 다 임포트 되어 하나의 컨트롤러 테스트가 너무 느려졌고, 단위 테스트의 의미가 사라지는거 같습니다.

혹시 이와 관련해서 좋은 방법이 없을까 해서 질문을 남깁니다.

그리고 @SpringBootTest 시에는 각각의 테스트 말고 전체를 테스트 할 때 @WebMvcTest 를 할 때처럼 스프링 컨텍스트가 여러개 떠서 테스트 속도가 느려지는 문제가 없을까요?

항상 @SpringBootTest 는 무겁다는 생각 때문에 잘 사용을 안해서 궁금하네요. 그리고 현업에서 @SpringBootTest 를 자주 사용하는지도 궁금합니다. 저는 주로 @WebMvcTest, @DataJpaTest 이정도를 사용하고 도메인 계층은 일반 클래스 처럼, 서비스 계층은 모킹만 해서 스프링 도움없이 테스트를 하고 있습니다.

답변 1

답변을 작성해보세요.

1

안녕하세요, opensesame님 ㅎㅎㅎㅎ 좋은 질문 감사드립니다~~ 우선 제가 상황과 질문을 잘 이해했는지 정리해보도록 하겠습니다!

[상황]

  1. @SpringBootTest는 잘 사용하지 않고 @WebMvcTest와 @DataJpaTest 정도를 사용하는 중. 도메인 계층은 POJO로 간주하여 테스트를, 서비스 계층은 repository를 mocking해 사용하는중

  2. @WebMvcTest를 각각 사용하여 전체 테스트를 돌리면, context가 여러 개 뜨는 문제로 인해 abstract class로 Bean 구성을 모아 하나의 context만 뜨도록 해둠

[질문]

  1. 위의 상황 2가 괜찮은 접근인지 궁금합니다.

  2. @SpringBootTest를 사용하면 context가 여러개 뜨는 일이 없는지(=이로 인해 속도가 느려지지는 않는지) 궁금합니다.

  3. 현업에서는 @SpringBootTest를 자주 사용하는지 궁금합니다.

크으~~ 좋습니다 ㅎㅎㅎㅎ 우선 고전파와 런던파, 그리고 좋은 테스트의 조건을 이야기 하지 않을 수 없습니다!

간단히 요약드려보면 고전파와 런던파가 바라보는 '단위 테스트'의 정의가 다르며, 저는 개인적으로 (사람마다 다를 수 있고, 같은 사람이더라도 상황에 따라 다른 판단을 해야 한다고 생각합니다!) 고전파에 속하기 때문에 mocking의 사용을 꼭 필요한 경우가 아니면 하지 않고 있습니다.

이런 관점에서 이제 답변을 드려보도록 하겠습니다 ㅎㅎㅎ

  1. 우선은 @WebMvcTest에서 특정 Controller만을 target하고 있어서 Bean 구성이 달라지는 것인지, 아니면 Controller에서 호출하는 Service를 그때마다 @MockBean으로 처리하고 있어 Bean 구성이 달라지는 것인지에 따라 의견이 조금 달라질 수 있을 것 같은데요, 만약 전자라면, 지금처럼 bean을 모으는 것보다 @SpringBootTest를 사용하시는 것도 좋을 것 같고요!! 후자라면 지금 사용하시는 방법도 좋다라고 생각합니다! 😊
    실제로 MockBean 등을 사용할 때 context 뜨는 비용을 줄이기 위해 환경을 통합하는 것은 꽤 흔하게 사용하는 방법입니다! 추가로 이러한 구성을 피하기 위해 잠시 Mocking Bean을 주입했다가 그 테스트가 끝나고는 정상적인 Singleton Bean을 주입하는 방법도 있습니다.

  2. @SpringBootTest를 사용하더라도 @MokBean 이나 @SpyBean 을 사용하다면, context가 여러개 뜰 수 있습니다! 그리고 컨텍스트가 뜨는 조건이 꽤 여러개라 다른 경우에도 있을 수 있어요! (테스트의 profile을 다르게 한다거나 등등…)

  3. 제가 경험했던 모든 환경에서는 @SpringBootTest 를 사용했습니다. 하지만 물론 제가 모든 회사, 모든 팀을 경험해보았던 것은 아니라 ‘자주' 사용하는지 까지는 잘 모르겠습니다 ㅎㅎㅎㅎ
    제 생각에는 팀마다, 회사마다 조금씩 다른 것 같아요. 예를 들어 저는 @Transactional 을 테스트와 함께 사용하는 것을 선호하지 않아 (https://www.youtube.com/watch?v=S_66BYHWT2A) @DataJpaTest를 쓰지 않습니다! 이 어노테이션에는 트랜잭션이 자동으로 걸려 있거든요! 😢
    @WebMvcTest 역시 컨트롤러 테스트를 잘 하지 않다보니 거의 사용하지 않습니다. (정말 꼭 필요한 경우에만 하고 있어요!)

추가로, mocking을 선호하시는 이유는 아마 테스트를 돌릴 때의 속도 차이가 크다고 생각됩니다! context를 사용하는 테스트의 경우는 한 번을 돌리더라도 context를 띄워야 하다보니 순수 클래스 테스트를 하는 것과는 비교가 안되게 느리거든요!! 😢 (순수 클래스 테스트가 1초라면, context를 띄우면 순식간에 10~20초가 더 걸리게 되죠..) 이것을 무겁다라고도 표현해주신것 같고요!

저 역시 opensesame 님과 비슷하게 생각합니다! 다만, 1) 리팩토링 내성 2) Spring Context가 없는 테스트가 정말 회귀 방지의 역할을 할 수 있는가에 대한 회의가 있어 모킹하지 않은 테스트를 선호하는 것 같습니다 ㅎㅎㅎ

그리고 속도적인 부분은 branch를 push하면 자동으로 테스트가 돌아가는 CI 환경을 구축한다거나, 수천개의 전체 테스트를 돌릴때 수 분내로 돌아갈 수 있도록 일부 튜닝을 한다거나 하는 방법으로 극복하려 노력하고 있습니다.

이 주제가 무척 방대하고 어렵다 보니 (좋은 테스트란 무엇인가, 어떻게 테스트를 해야 좋은가, 각 계층 각 기능별로 어떻게 테스트를 해야 Best Practice인가) 확실히 답변을 바로 드리기가 쉽지 않네요 ㅎㅎㅎㅎ

혹시라도 추가적으로 궁금하게 있으시면, 편하게 질문 부탁드려요~!!!

감사합니다!! 오늘도 좋은 하루 되세요!! 😊🙏

opensesame님의 프로필

opensesame

질문자

2022.08.28

주말임에도 불구하고 이렇게 열심히 답변해주셔서 감사합니다!

얼마전에 단위 테스트라는 책을 읽어 보았는데 그러면서 고전파쪽이 더 낫다고 개인적으로 생각하는데요.

회사에서는 로컬 개발환경 없이 개발 서버를 보고 모든 개발자들이 개발을 하다보니

어쩔 수 없이 모킹을 하고 테스트를 하고 있는 것 같습니다.

로컬에서 h2 를 사용할수는 있겠지만 실제 mysql 환경과 조금씩은 다른 차이가 있다고 있다고

알고 있어서 그것도 좋은 방법인지는 잘 모르겠습니다.

모킹을 해서 테스트를 하다보니 모킹을 하는 시간도 오래 걸리고 리팩토링 내성이 많이

떨어진다는걸 요즘 느끼고 있네요 ㅜ

모킹을 안하고 테스트를 작성 하는 것을 한번 팀내에서 같이 이야기 해봐야 할 거 같습니다 ㅎㅎ

좋은 답변 감사합니다! 좋은 하루 되세요! 😊

안녕하세요!! 아이고~ 그렇군요 ㅠㅠㅠ 개발 서버를 대상으로 테스트를 하시는군요!!

말씀해주신것처럼 H2를 쓰게 되면 MySQL을 사용하는 것과 일부 다르긴 하더라고요 ㅠㅠ 다만 테스트를 작성할 때 크게 체감되지 않았고 in절에 emptyList가 들어가는 경우만 의식적으로 주의한다면 지금까지 큰 문제는 없었습니다! (H2로 로컬 테스트를 약 3년 정도 해온 것 같습니다!!)

MySQL에는 emptyList가 in절에 들어가면 SQL에러가 나고, H2에서는 잘 되었습니다! (또 최신 버전에서는 다를 수 있을 것 같아요!)

그래서 요즘(?)에는 테스트를 돌리때 도커를 띄워 그 컨테이너 안에 mysql을 셋팅하고 mysql 대상으로 테스트를 돌리기도 한다고 하더라고요! 다만 아직 best practice를 찾지는 못해서 직접 적용해본 적은 없습니다 ㅎㅎㅎㅎ

팀원분들과의 커뮤니케이션!! 응원합니다 ㅎㅎㅎㅎㅎ

내일부터 시작되는 한 주도 화이팅 되세요~!!! 😊 감사합니다!!!