개요
- 평소 착각할 수 있는 junit 애노테이션을 비교해 보고자 한다.
@Mock vs @MockBean
- 공통점
- 둘 모두 특정부분을 단위테스트 하고 싶을때, 테스트하고 싶은 모듈에 의존성을 가지고 있는 모듈을 Mock객체로 대체하고 조작하여 테스트할 수 있다.
- 둘 모두 given, when. verify 등을 사용하여 행위를 테스트한다.
- @Mock
- 모키토 라이브러 내에 존재한다.
- Mock은 가짜객체를 만드는데 스프링빈에 등록이 안되는 객체이다.
- 스프링 컨테이너가 DI를 하는 방식이 아니라 객체생성시 생성자에 Mock객체를 직접 주입해준다.
- 생성자 주입을 사용해야 편하게 사용 가능합니다.
- 스프링을 띄우지 않으므로 MockBean을 사용할때보다 빠르다.
- @InjectMocks에 대해서만 해당 클래스안에서 정의된 객체를 찾아서 의존성을 해결한다.
- mocking 하지 않은 클래스는 절대 의존성이 주입되지 않는 것을 명심해야된다.
- @Autowired 로 주입받은 빈은 관련된 의존성이 자동으로 주입되는 것과 차이가 있다.
- 코드예시
@ExtendWith(MockitoExtension.class) class ServiceTest { private Service service; @Mock private Repository Repository; @BeforeEach void setUp() { service = new Service(repository); } @Test void findAll() { given(repository.findAll()).willReturn(...); List<String> actual = service.findAll(); }
- @MockBean
- 스프링부트 테스트 패키지 내에 존재한다.
- MockBean은 가짜 Bean을 스프링에 등록해달라는 의미이다.
- 스프링 컨테이너가 기존에 갖고있는 Bean객체는 MockBean객체로 치환되어 DI에 사용된다.
- mock 객체를 스프링 컨텍스트에 등록하는 것이기 때문에 @SpringBootTest를 통해서 Autowired에 의존성이 주입되게 된다.
@WebMvcTest(controllers = Controller.class) @AutoConfigureMockMvc class ContollerTest { private MockMvc mockMvc; @MockBean private Service service; @BeforeEach void setUp(WebApplicationContext webApplicationContext) { this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) .addFilter(new CharacterEncodingFilter("UTF-8", true)) .build(); } @Test void findAll() { given(service.findAll()).willReturn(...); mockMvc.perform(get("/") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk())) ... } }
- @WebMvcTest 이기 때문에 Controller까지는 로드 된다. -> 하지만 Service 는 가져오지 못한다. -> 그렇기 때문에 @MockBean 을 이용해서 service 를 가져올 수 있다.
- @Mock 으로 가져오면 안될까? -> service 가 실제로 기능이 수행돼야 하기때문에 Bean Container 에 있는 service 가 필요하다.
- @Mock 으로 만든다면 정상적인 기능 을 수행할 수 없다.
- 정리하면 아래와 같다.
- 정리하면 Bean이 Container에 의존할 경우에는 MockBean 을 사용하고, 그렇지 않을 경우에는 Mock을 사용한다.
@MockBean vs @SpyBean
- @MockBean
- MockBean은 해당 annotation이 붙은 객체를 mock 객체로 생성하고 Bean으로 등록한다.
- @MockBean으로 선언한 객체와 같은 이름과 타입으로 이미 빈으로 등록되어있다면 해당 빈은 선언한 Mock 빈으로 대체된다.
- @SpringBootTest를 통해 Mock Bean객체는 @Autowired로 주입 받을 수 있습니다.
- willReturn 이라는 함수를 통해 반환 값을 받을 수 있다.
- given을 통해 어떤 객체의 stub을 설정할지 지정하고, 뒤에 메서드를 호출했기 때문에 해당 method가 호출 되면 willReturn에 지정된 값이 반환된다는 뜻이다.
- 예시는 아래와 같다.
@SpringBootTest public class MockBeanTest { @Autowired TestService testService; @MockBean TestRepository testRepository; @Test void mockInjectionTest() throws Exception { // given Entity entity = Entity.builder() .id(1L) .name("name") .build(); willReturn(entity) .given(testRepository).save(entity); // when Long saveId = testService.save(entity); // then assertEquals(1L, saveId); Mockito.verify(testRepository, Mockito.times(1)).save(entity); } }
- @SpyBean
- @MockBean과의 차이점은 @SpyBean을 interface에 붙일 경우 interface의 구현체를 spring context 에 등록이 되어있어야 한다.
- MockBean인 경우 전체가 stub을 적어도 Default 값을 넣어서 stub을 형성하지만, Spy는 stub이 없는 경우 자동으로 구현된 메서드를 호출 하는 형태이기 때문이다.
- 예시는 아래와 같다.
@SpringBootTest public class SpyBeanTest { @Autowired TestService testService; @SpyBean TestRepository testRepository; @Test void spyBeanTest() throws Exception { // given Entity entity = Entity.builder() .id(1L) .name("name") .build(); willReturn(Collections.emptyList()) .given(testRepository).findAll(); // when Long saveId = testService.save(entity); Entity findEntity = testService.findById(1L); List<Entity> list = testService.findEntiy();
- @MockBean과의 차이점은 @SpyBean을 interface에 붙일 경우 interface의 구현체를 spring context 에 등록이 되어있어야 한다.
// then
assertEquals(0, list.size());
assertEquals(1L, saveId);
assertEquals(1L, findEntity.getId());
Mockito.verify(testRepository, Mockito.times(1)).findAll();
}
}
<br><br><br>
# @ExtendWith(SpringExtension.class) vs @ExtendWith(MockitoExtension.class)
- @ExtendWith(SpringExtension.class)
- SpringContainer를 로드하므로 Test 객체에 @Autowired를 통해 Bean 의존성을 주입시킬 수 있다.
- 또한 Bean을 Mocking하기위한 @MockBean 기능을 사용할 수 있다.
- 테스트를 위해 Spring이 필요하다면 위 코드가 필요하다.
- @ExtendWith(MockitoExtension.class)
- SpringContainer를 로드하지않고 테스트를 위한 기능만 제공한다.
- @Mock, @Spy 기능을 사용할 수 있다.
- 테스트에 Spring이 필요없이 순수한 단위 테스트만 필요하다면 위 코드를 추가하면 된다.
<br><br><br>
## REFERENCES
- https://frozenpond.tistory.com/83
- https://beomseok95.tistory.com/303
- https://cobbybb.tistory.com/16
- https://dundung.tistory.com/230
- https://clack2933.tistory.com/m/28
'Junit' 카테고리의 다른 글
MockMvc (0) | 2022.02.27 |
---|---|
Mockito (0) | 2022.02.27 |
# Junit 5 기본 애노테이션 (0) | 2022.02.27 |
Controller 단위테스트 (0) | 2022.02.27 |
Bad Request 처리하기 (0) | 2022.02.27 |