2021. 8. 12. 17:13ㆍSpring
BDDMockito VS Mockito
- 최근 테스트 코드를 작성하는 것에 재미가 들려서 내가 이해할겸 정리해본다.🥸
순수 Mockito에서 BDD의 Given / When / Then을 위해서 다음과 같이 when( obj ) 메서드와 thenReturn() 이용하고 verify() 구문을 이용해 검증한다.
이런식으로 특정 상황에 대한 (when과 같이) 우리가 가짜로 결과를 만들어 주는 것을 Stubbing(스터빙) 이라고 한다. 즉, 가짜로 수행할 객체를 넣어주는 것이다.
when(phoneBookRepository.contains(momContactName)) .thenReturn(false); phoneBookService.register(momContactName, momPhoneNumber); verify(phoneBookRepository) .insert(momContactName, momPhoneNumber);
- 위에 코드는 워닛블로그 님 코드를 참조하였습니다!
위에 코드는 Stubbing(스터빙)의 과정인데, 메서드 이름이 뭔가... 헷갈린다..🤔
- When()
- ThenReturn()
- Verity()
개념적으로 BDDMockito에 given에 해당되는 Mockito의 when()의 이름이 when이라 쉽게 헷갈릴 수 있다..(아니 나 너무 헷갈렸어.. 다른 의미인가 하고ㅠㅠㅠ)
BDDMockito의 given() 메서드를 이용해서 해결하자!!!
BDDMockito를 이용하면 Mockito의 when() 을 given() 이라는 메서드로 더 정확한 의미 전달 할 수 있다.!!!
given(someClass.method()).willReturn()
given() 메서드
- given()의 파라미터를 이용해서 어떤 상황에 즉, 어떤 메서드가 실행되었을 때의 테스트를 위한 상황을 설정할 수 있다.
예를 들어보자
public interface MemberRepository extends JpaRepository<Member, Long> {
boolean existsByEmail(String email);
}
public class MemberService {
@Autowired
private MemberRepository memberRepository;
public boolean isExistEmail(String email) {
return memberRepository.existsByEmail(email); // 존재하면 true, 존재하지 않으면 false 반환
}
}
public interface MemberRepository extends JpaRepository<Member, Long> {
boolean existsByEmail(String email);
}
public class MemberService {
@Autowired
private MemberRepository memberRepository;
public boolean isExistEmail(String email) {
return memberRepository.existsByEmail(email); // 존재하면 true, 존재하지 않으면 false 반환
}
}
public class MemberServiceTest {
@MockBean
private MemberService memberService;
private final MemberRepository memberRepository = mock(MemberRepository.class);
@BeforeEach
void stubbing() {
given(memberRepository.existsByEmail(any(String.class)))
.willReturn(false);
}
}
- 위에 코드는 워닛블로그 님에서 참고하였습니다.
given(memberRepository.existsByEmail
(any(String.class)
)).willReturn(false)
;
- 3개 부분으로 나눌 수 있다.
- memberRepository.existsByEmail - Mocking 할 메서드
- any(String.class) - 메서드의 파라미터
- willReturn(false) - 해당 메서드가 반환하는 값
자, 그럼 3가지를 정리해보자
Mocking 할 메서드
그래서 Mocking 메서드가 뭐야?
- 우리는 지금 MemberService를 테스트를 하려한다. Unit Test에서 중요한 것은 테스트하려는 대상의 고립이다.
여기서 테스트 대상을 고립한다는 뜻은, 테스트 대상에 연관된 다른 객체들은 관여하지 않도록 우리가 가짜 객체를 넣어줘야 한다는 소리와 비슷!!
이렇게 테스트 대상을 고립하기 위해서 Mockito의 mock()을 이용하였고, 테스트 대상이 특정 결과를 수행하기 위해 연관된 객체의 연산을 주입해주면 된다.
- 즉 MemberService 에는 현재 isExistByEmail 메서드가 존재하고 해당 메서드 내부에 MemberRepository 인스턴스가 existsByEmail 연산을 수행하고 있으므로 우리는 MemberRepository의 existsByEmail을 가짜로 주입해주면 된다.
해당 메서드의 파라미터
해당 메서드는 existsByEmail 이라는 메서드는 String 으로 email을 받고 있다.
다음과 같은 선택지가 존재한다.
특정 값을 받았을 때의 행동 정의
모든 값을 받았을 때의 행동 정의
특정 값을 받았을 때의 행동 정의하기, eq()
- 위의 상황에서는 모든 String을 받았을 때의 행동을 정의하는데, 만약 특정한 String 값이 들어와야 한다면??
그럴때 사용하는게 eq( ) 메서드이다.- eq( ) : 동일한 값 혹은 객체의 행동
모든 값을 받았을 때의 행동 정의하기 , any()
모든 객체가 들어왔을 때의 행동은 any(Object object) 를 이용할 수 있다. 여기서는 String 인 모든 객체가 가능하다.
위와 같은 상황에 ( 어떤한 String 값이라도 가능한 상황 ) 사용할 수 있는 메서드가 여럿 존재함.
- anyString() : 어떠한 문자열
- anyLong() : 어떠한 Long
- anyBoolean() : 어떠한 Boolean 값
- any() : 모든 객체
public class MemberServiceTest { // ... 생략 @BeforeEach void stubbing() { given(memberRepository.existsByEmail(anyString())); given(memberRepository.findById(anyLong())); given(memberRepository.findAllByOffline(anyBoolean())) .willReturn(false); } }
- 위의 상황에서는 모든 String을 받았을 때의 행동을 정의하는데, 만약 특정한 String 값이 들어와야 한다면??
해당 메서드를 수행했을 때 반환하는 값
- 위와 같이 다양한 방법으로 행동을 정의하였다. 그럼 이제 해당 행동에 적절한 반환(3가지 반환 방법)을 해줘야 한다.
- willReturn( )
- will( )
- willThrow( )
willReturn( )
- 밑에 코드들은 워닛블로그 님 코드를 참고하였습니다.
public class MemberServiceTest {
// ... 생략
@BeforeEach
void stubbing() {
given(memberRepository.existsByEmail(any(String.class)))
.willReturn(false);
}
@Test
void exists_valid() {
boolean isExist = memberService.isExistEmail("valid@gmail.com");
assertTrue(isExist);
}
}
- 뜻 그대로 반환 값을 명시해줄 수 있다.
will( ) + invocation
will( ) 은 willReturn과는 조금 다르다. willRetrun은 고정된 값을 반환하는데, will( ) 에서는 invocation 을 통해서 새로운 객체를 반환하거나 아예 새로운 행동을 반환할 수 있다.
public class MemberServiceTest { // ... 생략 @BeforeEach void stubbing() { given(memberRepository.findById(anyLong())) .will(invocation -> { Member member = invocatoin.getArgument(0); return MemberData.builder() .email("test123@gmail.com") .isFound(true) .build(); }); } @Test void exists_valid() { MemberData memberData = memberService.findMember(1L); assertNotNull(memberData); } }
willThrow( )
말 그대로 예외를 던진다.
public class MemberServiceTest { @BeforeEach void stubbing() { given(memberRepository.existsByEmail(eq("hello@gmail.com"))) .willThrow(MemberDuplicationException("exists"); } @Test void exists_valid() { MemberDuplicationException exeption = assertThrow( MemberDuplicationException.class, () -> memberService.isExistEmail("valid@gmail.com") ); assertEquals(exception.getMessage(), "exists"); } }
'Spring' 카테고리의 다른 글
RequiredArgsConstructor VS Qualifier 빈 주입 (0) | 2022.05.15 |
---|---|
인터페이스 빈 주입을 해야하는 이유 (0) | 2022.05.15 |
[Spring] TDD VS BDD (0) | 2021.08.12 |
@Mock객체를 사용해서 Page타입 객체 테스트코드 작성하기 (3) | 2021.07.29 |
[Spring Boot] Spring Auditing 적용해보기 (0) | 2021.06.02 |