Mock이란?
목업(Mock-up)은 실제 제품이 나오기 전에 내부적으로 사용하기 위한 모형(가짜) 제품을 의미한다.
단위 테스트나 슬라이스 테스트 등에 Mock 객체를 사용하는 것을 바로 Mocking이라고 한다.
Mockito란?
Mockito란 Mock 객체를 생성하고, 해당 Mock 객체가 진짜처럼 동작하게 하는 역할을 해주는 Spring Framework의 라이브러리이다.
Mockito의 Mocking 기능을 이용해서 테스트하고자 하는 대상에서 다른 영역(다른 계층 또는 외부 통신이 필요한 서비스 등)을 단절시켜 오로지 테스트 대상에만 집중할 수 있다.
Stubbing이란?
Stubbing은 테스트를 위해서 Mock 객체가 항상 일정한 동작을 하도록 지정하는 것을 의미한다.
MemberController의 postMember() 테스트에 Mockito 적용
@SpringBootTest
@AutoConfigureMockMvc
public class MemberControllerMockTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private Gson gson;
@MockBean // Application Context에 등록되어 있는 Bean에 대한 Mockito Mock 객체를 생성하고 DI 한다.
private MemberService memberService;
@Autowired // MockMemberService의 createMember()에서 리턴하는 Member 객체를 생성하기 위해 DI 받는다.
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); // mapper를 이용해 Dto를 Entity 객체로 변환
member.setStamp(new Stamp()); // ResponseDto 변환 시 Stamp 정보도 포함 되어야 하므로 추가
/**Stubbing 메서드 : Mock 객체가 항상 일정한 동작을 하도록 지정하는 것.
* given()은 Mock 객체가 특정 값을 리턴하는 동작을 지정
* Mockito.any()는 createMember()의 파라미터인 Member 타입을 지정
* .willReturn()은 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());
}
}
@MockBean
- Application Context에 등록되어 있는 Bean에 대한 Mockito Mock 객체를 생성하고 주입해주는 역할
- 필드에 추가하면 해당 필드의 Bean에 대한 Mock 객체를 생성한 후, 필드에 주입(DI) 한다.
given(memberService.createMember(Mockito.any(Member.class)))
- given()은 Mock 객체가 특정 값을 리턴하는 동작을 지정하는데 사용하며, Mockito에서 지원하는 when()과 동일한 기능을 한다.
- Mock 객체인 memberService 객체로 createMember() 메서드를 호출하도록 정의했다.
- .willReturn(member)은 createMember() 메서드가 리턴 할 Stub 데이터이다.
MemberService의 createMember() 테스트에 Mockito 적용
조회된 Member 객체가 null이면 BusinessLogicException 을 잘 던지는지 여부를 테스트한다.
Mocking을 통해서 Member 객체를 제공할 수 있다.
@ExtendWith(MockitoExtension.class) // Spring을 사용하지않고 JUnit에서 Mockito 기능 사용
public class MemberServiceMockTest {
@Mock // 해당 객체를 Mock 객체로 생성한다.
private MemberRepository memberRepository;
@InjectMocks // 해당 객체에 @Mock 객체를 주입해준다.
private MemberService memberService;
@Test
public void createMemberTest() {
// given
Member member = new Member("hgd@gmail.com", "홍길동", "010-1111-2222");
/** Mock 객체(memberRepository)를 Stubbing한다.
* findByEmail과 Optional.of(member)의 이메일 주소가 동일하기 때문에 passed */
given(memberRepository.findByEmail(member.getEmail())).willReturn(Optional.of(member));
// when, then
assertThrows(BusinessLogicException.class, () -> memberService.createMember(member));
}
}
@ExtendWith(MockitoExtension.class)
- Spring을 사용하지 않고, Junit에서 Mockito의 기능을 사용할 수 있다.
@Mock
- 해당 필드의 객체를 Mock 객체로 생성합니다.
@InjectMocks
- 애너테이션을 추가한 필드에 생성한 Mock 객체를 주입해 줍니다.
TDD(Test Driven Development)란?
도메인 모델이 DDD의 중심에 서 있다면 TDD에는 테스트가 그 중심에 서 있다.
TDD의 개념을 한마디로 요약하자면 ‘테스트를 먼저 하고 구현은 그 다음에 한다’ 로 요약할 수 있다.
백엔드 개발자의 일반적인 개발 흐름
- 이해 당사자들 간에 수집된 요구 사항과 설계된 화면(UI 설계서 등) 등을 기반으로 도메인 모델을 도출한다.
- 도출된 도메인 모델을 통해 클라이언트의 요청을 받아들이는 엔드포인트와 비즈니스 로직, 데이터 액세스를 위한 클래스와 인터페이스 등을 설계해서 큰 그림을 그려본다.
- 클래스 설계를 통해 애플리케이션에 대한 큰 그림을 그려보았다면 클래스와 인터페이스의 큰 틀을 작성한다.
- 클래스와 인터페이스의 큰 틀이 작성되었다면 클래스와 인터페이스 내에 메서드를 정의하면서 세부 동작을 고민하고, 코드로 구현한다.
- 해당 메서드의 기능 구현이 끝났다면 구현한 기능이 잘 동작하는지 테스트 한다.
- 테스트에 문제가 발생한다면 구현한 코드를 디버깅하면서 문제의 원인을 찾는다.
TDD의 개발 흐름
- TDD는 모든 조건에 만족하는 테스트를 먼저 진행한 뒤에 조건에 만족하지 않는 테스트를 단계적으로 진행하면서 실패하는 테스트를 점진적으로 성공시켜 간다.
- TDD는 테스트 실행 결과가 “failed”인 테스트 케이스를 지속적으로 그리고 단계적으로 수정하면서 테스트 케이스 실행 결과가 “passed”가 되도록 만든다.
- TDD는 테스트가 “passed” 될 만큼의 코드만 우선 작성한다.
- TDD는 ‘실패하는 테스트 → 실패하는 테스트를 성공할 만큼의 기능 구현 → 성공하는 테스트 → 리팩토링 → 실패하는 테스트와 성공하는 테스트 확인’ 이라는 흐름을 반복한다.
'Spring Boot > MVC' 카테고리의 다른 글
[Spring MVC] application.yml 프로파일 기능 사용법 (0) | 2022.09.16 |
---|---|
[Spring MVC] API 문서화 - Spring Rest Docs (0) | 2022.09.15 |
[Spring MVC] 테스팅(Testing) (0) | 2022.09.08 |
[Spring MVC] 트랜잭션(Transaction) (0) | 2022.09.06 |
[Spring MVC] Spring Data JPA 데이터 액세스 계층 구현 (0) | 2022.09.03 |