간단하게 말하면 가짜 혹은 진짜인 것처럼 보이는 물건이라고 말할 수 있다.
테스트에서 Mock이란 가짜 객체를 말하고, Mocking이란 Mock 객체를 사용해서 진짜처럼 보이게 하는 것을 말한다.
슬라이스 테스트에서 사용한다!
보통 한 계층은 다른 계층들과 연동되어 있는데 슬라이스 테스트는 해당 계층에 대해서만 테스트를 진행하는 것이므로 다른 계층과의 연동을 끊어줘야 한다.
이때 연동을 끊어주는 역할을 Mock 객체가 한다.
@SpringBootTest
+ @AutoConfigureMockMvc
: Spring 애플리케이션에서 사용하는 빈을 불러와서 Application Context에 등록해준다.
@MockBean
: Application Context에 등록되어 있는 Bean에 대한 Mock 객체를 생성하고 필드에 주입해주는 역할을 한다.
@Autowired
: Application Context에 등록되어 있는 Bean을 가져와서 사용한다. (Bean을 그대로 가져온 것! Mock 객체가 아니다!)
@ExtendWith(MockitoExtension.class)
: Spring을 사용하지 않고, Junit에서 Mockito의 기능을 사용할 수 있게 해준다.
(Application Context는 생성되지 않음!)
→ Spring을 사용하지 않고 = 스프링 환경 위에서 테스트 하지 않겠다.
@Mock
: 해당 필드의 객체를 Mock 객체로 생성
@InjectMocks
: 해당 필드에 @Mock
을 통해 생성한 Mock 객체를 주입해준다.
@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerMockTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private MemberService memberService;
@MockBean
private MemberMapper mapper;
mockMvc
는 Controller를 사용하기 위해서 @Autowired
로 주입받는다.
@SpringBootTest
를 작성했기 때문에 @MockBean
애너테이션을 사용해서 memberService
를 Mock 객체로 만든다.
mapper는 @MockBean
이던 @Autowired
이던 상관없다.
→ 하지만 되도록이면 @MockBean
을 사용하자!
💡 왜 mapper는
@MockBean
이던@Autowired
이던 상관없나요?
@MockBean
로 하면, MemberMapper를 Mock 객체로 만들어서 항상 Stub 데이터를 리턴하도록 하므로 시간이 적게 걸린다.
그에 비해@Autowired
로 하면 실제 mapper를 통해서 실행시키는 것이므로 시간이 오래 걸린다.
(mapper에서 DB에 접근해서 데이터를 가져오기도 하기 때문에 시간이 많이 걸릴 수 밖에 없다.)
→@MockBean
과@Autowired
은 성능 면에 차이가 있다.
→ Mock 객체로 만들면 시간이 적게 걸리고, Autowired로 주입 받으면 시간이 오래걸린다.
@ExtendWith(MockitoExtension.class)
public class MemberServiceMockTest {
@Mock
private MemberRepository memberRepository;
@InjectMocks
private MemberService memberService;
@ExtendWith(MockitoExtension.class)
를 추가해서 Spring을 사용하지 않고 Mockito를 사용하도록 한다.@Mock
을 통해 Mock 객체로 만들어준 다음에 @InjectMocks
를 통해 MemberService에 주입해줘야 한다.💡 Service 계층에서
@SpringBootTest
애너테이션을 안 붙인 이유는?
-> 딱히 Spring에 의존적인 것이 없기 때문에!
Spring Bean들을 많이 사용해야 한다면 붙이는 것이 좋을 것 같다.
when()
과 동일한 기능을 한다.given(Mock 객체의 메서드 호출).willReturn(Stub 데이터)
💡 Stubbing?
테스트를 위해서 Mock 객체가 항상 일정한 Stub 데이터를 리턴하도록 하는 것
✅
@MockBean
과@Autowired
은@SpringBootTest
+@AutoConfigureMockMvc
처럼 Application Context를 생성해줄 수 있는 애너테이션이 있어야 사용할 수 있다!
"OrderService 클래스에는 @Service 애너테이션이 붙어 있고, Bean에 등록되었으니 당연히 @Autowired로 주입받을 수 있는 거 아닌가?" 라고 생각했는데 아니였다!
실제 Spring 애플리케이션에서의 Application context에는 Bean으로 등록되어 있으나 Test에서는 아니다.
(나는 Test도 Spring 애플리케이션의 일부라 같이 Application context를 공유하는지 알았음)
✅
@Test
이 붙은 테스트 클래스는 실제 애플리케이션이 실행되는 것이 아니라 단순히 테스트를 실행하는 것이기 때문에 Application Context가 만들어지지 않는다.
만약에 Test 클래스에@Autowired
를 이용해서 DI를 받고 싶다면 DI로 주입 받을 객체가 있는 Application Context가 있어야 한다.
가장 쉬운 방법은 @SpringBootTest
애너테이션을 추가하는 것인데 이건 클래스 하나를 테스트 하기위해 애플리케이션 전체를 실행시키는 것과 동일한 비용이 드는 작업이라서 권장되지 않는 방법이다.
Q1) @SpringBootTest
을 Controller가 아닌 다른 계층에 사용해도 되는 걸까?
➡️ 사용해도 된다!
Q2) @WebMvcTest
은 Controller에 관련된 Bean들만 가져오는데, 다른 계층인 Service와 데이터 액세스 계층은 어떻게 Bean들을 가져오는 것일까?
위와 같은 궁금증이 생겨서 구글링을 해보았다.
@SpringBootTest
+ @AutoConfigureMockMvc
: 전체 Application Context를 가져온다.
@WebMvcTest
: request나 response를 다루는 web layer 부분의 Application Context만 가져온다.
즉, @Controller
나 @RestController
가 붙은 클래스의 핸들러 메서드를 테스트할 때 사용한다.
(@Service, @Component, @Repository 등은 불가)
@WebMvcTest와 @AutoConfigureMockMvc의 차이점
@WebMvcTest에 대한 자세한 설명
서비스 계층은 Mocking을 사용하는 경우가 아니라면, configuration에 대해 독립적인 비지니스 로직이므로 어떠한 애너테이션도 붙이지 않고 테스트하는 것이 이상적이다.
➡️ 굳이 Spring에 의존하지 않고 직접 Service 객체를 new로 생성해서 사용하는 것이 오히려 테스트 비용을 줄일 수 있다.
@DataJpaTest
: 오직 JPA의 configuration만 가져온다.(JPA 테스트와 연관된 config만 적용한다.)
@Component
이 붙은 클래스를 Application Context로 로드 하지 않는다.(@ComponentScan
이 없다.)@SpringBootTest
+ Memory DB 연결
실무에서는 통합 테스트가 아닌 한 @SpringBootTest
를 거의 사용하지 않는다. 직접 new 객체로 생성해서 쓰거나 다른 애너테이션을 통해서 Application context를 구성하는 것이 좋은 방법인 것 같다.
[참고]
https://stackoverflow.com/questions/59097035/springboottest-vs-webmvctest-datajpatest-service-unit-tests-what-is-the-b
@SpringBootTest vs @DataJpaTest
1. @SpringBootTest와 @WebMvcTest는 같이 사용될 수 없다.
각자 MockMvc를 모킹하기 때문에 충돌이 발생한다!
[참고]
https://velog.io/@lxxjn0/Mockito%EC%99%80-BDDMockito%EB%8A%94-%EB%AD%90%EA%B0%80-%EB%8B%A4%EB%A5%BC%EA%B9%8C
40. 테스트
https://cornswrold.tistory.com/479
(-> 이 분은 나와 비슷한 에러를 겪으신 것 같다.)
[나중에 읽어볼 블로그]
블로그에서 설명하신 부분을 이해하고 싶은데, 아직 지식이 부족해서 이해가 안된다....ㅎ