Mock : 진짜 객체와 비슷하게 동작하나 프로그래머가 직접 그 객체 행동을 관리하는 객체
Mockito : Mock 객체를 쉽게 만들고 관리하고 검증할 수 있는 방법을 제공한다.
인터페이스를 구현한 구현체 클래스가 없다면 테스트가 어렵다.
이와 같은 상황에서 Mock 객체를 만들 수 있다.
@ ExtendWith (MockitoExtension .class ) // (1). Mock 어노테이션을 처리해줄 Extension을 추가해야함
class StudyServiceTest {
@ Mock
protected StudyRepository studyRepository ; // (2). StudyRepository 타입의 가짜 객체
@ Mock
protected MemberService memberService ; // (3). MemberService 타입의 가짜 객체
@ Test
void createStudyService () {
StudyService studyService = new StudyService (memberService , studyRepository );
assertNotNull (studyService );
}
}
모든 Mock 객체는 null 혹은 Optional.empty를 리턴한다.
Mock 객체를 조작해서 특정한 매개변수를 받은 경우 특정 값을 리턴하거나 예외를 던지도록 만들 수 있다.
Mock 객체를 조작해서 Void 메서드 특정 매개변수를 받거나 호출된 경우 예외를 발생시킬 수 있다.
Mock 객체를 조작해서 메서드가 동일한 매개변수로 여러번 호출될 때 각기 다르게 행동하도록 조작할 수 있다.
@ ExtendWith (MockitoExtension .class )
class StudyServiceTest {
@ Mock
protected StudyRepository studyRepository ;
@ Mock
protected MemberService memberService ;
@ Test
void createStudyService () {
Member member = new Member ();
member .setId (1L );
member .setEmail ("[email protected] " );
// Stubbing : Mockito를 사용해 특정 메서드 호출에 대해 미리 정의된 값을 반환하도록 설정한 작업
when (memberService .findById (1L )).thenReturn (Optional .of (member ));
StudyService studyService = new StudyService (memberService , studyRepository );
Study study = new Study (10 , "Java" );
studyService .createNewStudy (1L , study );
Optional <Member > findById = memberService .findById (1L );
assertEquals ("[email protected] " , findById .get ().getEmail ());
}
@ Test
void practice () {
// given
Member member = new Member ();
member .setId (1L );
member .setEmail ("[email protected] " );
Study study = new Study (10 , "테스트" );
// when
// TODO) memberService 객체에 findById 메서드를 1L 값으로 호출하면 Optional.of(member) 객체를 리턴하도록 Stubbing
// TODO) studyRepository 객체에 save 메서드를 호출하면 study 객체를 리턴하도록 Stubbing
when (memberService .findById (1L )).thenReturn (Optional .of (member ));
when (studyRepository .save (study )).thenReturn (study );
StudyService studyService = new StudyService (memberService , studyRepository );
studyService .createNewStudy (1L , study );
// then
assertNotNull (study .getOwnerId ());
assertEquals (member .getId (), study .getOwnerId ());
}
}
Mock 객체가 어떻게 사용이 되는지를 확인할 수 있다.
특정 메서드가 특정 매개변수를 받아서 몇 번 호출되었는지, 최소 한 번은 호출됐는지, 아니면 전혀 호출되지 않았는지
BDD : 애플리케이션이 어떻게 행동해야하는지에 대한 공통된 이해를 구성하는 방법으로, TDD에서 창안된 방법이다.
유명한 방법 : Given / When / Then
Given
: 어떤 상황이 주어졌을 때...
When
: 뭔가를 하면...
Then
: 이럴 것이다...
// Given -> When : Given이 만족되었을 때, When을 실행한다.
given (memberService .findById (1L )).willReturn (Optional .of (member )); // memberService의 findById(1L) 메서드가 호출되면, Optional.of(member) 객체를 반환하도록 설정한다
given (studyRepository .save (study )).willReturn (study ); // studyRepository의 save(study) 메서드가 호출되면, study 객체를 그대로 반환하도록 설정한다.
// Then -> Verify : Then 단계에서 Verify 한다.
then (memberService ).should (times (1 )).notify (study ); // memberService의 notify(study) 메서드가 정확히 1번 호출됐는지 검증한다.
then (memberService ).shouldHaveNoMoreInteractions (); // memberService에 대해 더 이상 아무런 상호작용(메서드 호출)이 없었는지 검증한다.
📚 Mockito vs BDDMockito API