[Spring] Mockito, Mock 이란?
Mock :
가짜 객체
Mocking :
단위 테스트, 슬라이스 테스트 등에 Mock 객체 사용하는 것
테스트에서 Mock 객체를 사용하는 이유 :
일반 Test는 완전한 슬라이스 테스트라 보기 힘들다!
Controller에 있는 핸들러 메서드 테스트를 돌리는 것이지만,
결국 그 핸들러 메서드가 서비스 계층을 거치고, db까지 타고타고 갔다가 돌아오는 것이기 때문에
슬라이스 테스트라기보다는 통합 테스트에 가깝다.
but 슬라이스 테스트의 목적 = 해당 계층 영역 테스트에 집중하는 것!
Mock 객체를 사용하면 테스트 시에
'다른 계층과 단절'시켜주어 불필요한 과정을 줄일 수 있다.
테스트하려는 대상에 집중해서 테스트 수행 가능!
Mockito?🍸
: Spring Framework 자체적으로 지원하는 Mocking 라이브러리
Mock 객체를 생성하고, 해당 Mock 객체가 진짜처럼 동작하게 하는 기능을 한다.
ex 1) HelloController 의 postHello() 핸들러 메서드 테스트에 Mockito 적용
: Controller 만 테스트를 하고 싶은 것이기 때문에,
Service 를 Mock 으로 만들어주면 연관 관계를 단절 시킬 수 있다!
Mockito로 Service 계층을 Mock 객체로 만들어주고,
그에 따라 리턴받는 형식들도 Mock 객체로 받아주면 됨
@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerMockTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private Gson gson;
// @MockBean :
// ApplicationContext 에 등록되어 있는 Bean에 대한 Mockito Mock 객체 생성, 주입
@MockBean
private MemberService memberService;
// createMember 리턴하는 Member 객체 생성 위해 DI
@Autowired
private MemberMapper mapper;
@Test
void postMemberTest() throws Exception {
// < given >
MemberDto.Post post = new MemberDto.Post("hgd@gmail.com",
"홍길동",
"010-1234-5678");
Member member = mapper.memberPostToMember(post); //변수 Member객체로 변환
member.setStamp(new Stamp());
// Mockito - Stubbing 메서드
// given() : Mock 객체가 특정 값 리턴 동작 지정에 사용 = when()
// .willReturn(member) : Mock의 createMember()가 리턴할 Stub 데이터
given(memberService.createMember(Mockito.any(Member.class)))
.willReturn(member);
String content = gson.toJson(post);
// < when >
ResultActions actions =
mockMvc.perform(
post("/v11/members")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(content)
);
// < then >
MvcResult result = actions
.andExpect(status().isCreated())
.andExpect(jsonPath("$.data.email").value(post.getEmail()))
.andExpect(jsonPath("$.data.name").value(post.getName()))
.andExpect(jsonPath("$.data.phone").value(post.getPhone()))
.andReturn();
// System.out.println(result.getResponse().getContentAsString());
}
}
Stubbing 이란 ? : 테스트를 위해 Mock 객체가 항상 일정한 동작을 하도록 지정하는 것
- MockMemberService 의 createMember() 가 리턴할 Stub 데이터
➡ 실제 MemberService 의 createMember() 가 호출되지 않고,
Mockito 가 생성한
MockMemberService 의 createMember() 가 호출된다! (디버깅, breakpoint로 확인 가능)
ex 2) HelloService 의 createHello() 테스트에 Mockito 적용
: Service 계층은 비즈니스 로직이다.
보통의 Service 계층의 메서드들은 DB의 데이터 조회를 통해 특정 데이터가 있는지 없는지 검증하는데,
(조회된 객체가 null 인지 아닌지..)
슬라이스 테스트를 위해서는 데이터 액세스 계층과의 연결을 단절시키면 된다.
➡ Mockito를 통해서!
// @ExtendWith(어쩌구.class) - Spring 사용 X, JUnit 에서 Mockito 기능 사용 위함
@ExtendWith(MockitoExtension.class)
public class MemberServiceMockTest {
@Mock //@Mock : 해당 필드 객체를 Mock객체로 생성
private MemberRepository memberRepository;
@InjectMocks //@InjectMocks : 어노테이션 추가된 필드에 ⬆@Mock 객체를 주입해줌
private MemberService memberService;
// = MemberService 에 memberRepository Mock 가 주입됨!
@Test
public void createMemberTest() {
// < given >
Member member = new Member("hgd@gmail.com", "홍길동", "010-1111-1111");
//memberRepository Mock - Stubbing
given(memberRepository.findByEmail(member.getEmail()))
.willReturn(Optional.of(member)); //리턴값 지정
// < when / then >
assertThrows(BusinessLogicException.class, () -> memberService.createMember(member));
}
}
Optional.of(member)의 member 객체의 이메일 주소
==
memberService.createMember(member)의 member 객체의 이메일 주소
라서 테스트는 pass!
@MockBean : Application Context에 등록되어 있는 Bean에 대한 Mockito Mock 객체를 생성하고 주입해주는 역할
@ExtendWith(MockitoExtension.class)를 클래스 레벨에 추가
: Junit에서 Spring을 사용하지 않고 순수하게 Mockito의 기능만을 사용하기 위함
@Mock : 해당 필드의 객체를 Mock 객체로 생성
@InjectMocks : ⬅ 추가한 필드에 @Mock 애너테이션을 통해 생성된 Mock 객체 주입