구현체는 없지만 인터페이스만 있고, 그 인터페이스를 기반으로 작성한 코드의 테스트가 필요한 상황.
구현체가 있다면 직접 쓰거나 Mock 하면 되지만, 구현체가 없는 경우에는 Mock 할 수 밖에 없다.
구현체 없이 인터페이스만 가지고 Mock을 사용하지 않았을 때 테스트를 작성하면 아래와 같다.
@Test
void createStudyService() {
MemberService memberService = new MemberService() {
@Override
public Optional<Member> findById(Long memberId) {
return Optional.empty();
}
.
.
.
};
StudyRepository studyRepository = new StudyRepository() {
@Override
public List<Study> findAll() {
return null;
}
.
.
.
};
StudyService studyService = new StudyService(memberService, studyRepository);
assertNotNull(studyService);
}
4. Mock 객체 Stubbing
모든 Mock 객체의 행동
null을 리턴한다. (Optional 타입은 Optional.empty 리턴)
Primitive 타입은 기본 Primitive 값.
콜렉션은 비어있는 콜렉션.
Void 메소드는 예외를 던지지 않고 아무런 일도 발생하지 않는다. (아무런 일도 일어나지 않는다.)
Mock 객체를 조작해서
특정한 매개변수를 받은 경우 특정한 값을 리턴하거나 예외를 던지도록 만들 수 있다.
How about some stubbing?
Argument matchers
void 메소드 특정 매개변수를 받거나 호출된 경우 예외를 발생시킬 수 있다.
Subbing void methods with exceptions
메소드가 동일한 매개변수로 여러 번 호출될 때 각기 다르게 행동하도록 조작할수도 있다.
Stubbing consecutive calls
예시
mokito의 when()을 가지고 mock 객체로 stubbing을 할 수 있다. (주석도 참고)
member.setId(1L); 의 값이 달라지면 when(memberService.findById(1L))에서 아무것도 하지 않는다.
그렇기 때문에 anyLong() 등을 사용할 수도 있다.
@Test
void createNewStudy(@Mock MemberService memberService,
@Mock StudyRepository studyRepository) {
StudyService studyService = new StudyService(memberService, studyRepository);
assertNotNull(studyService);
Member member = new Member();
member.setId(1L);
member.setEmail("tony@email.com");
when(memberService.findById(1L)).thenReturn(Optional.of(member));
Study study = new Study(10, "java");
// 아래 코드를 호출한 순간 -> Mock을 위해 작성한 코드가 실행
Optional<Member> findById = memberService.findById(1L);
assertEquals("tony@email.com", findById.get().getEmail());
}
Study study = new Study(10, "테스트");
// TODO memberService 객체에 findById 메소드를 1L 값으로 호출하면 Optional.of(member) 객체를 리턴하도록 Stubbing
// TODO studyRepository 객체에 save 메소드를 study 객체로 호출하면 study 객체 그대로 리턴하도록 Stubbing
studyService.createNewStudy(1L, study);
assertEquals(member, study.getOwner());
@Test
void createNewStudy() {
// Given
StudyService studyService = new StudyService(memberService, studyRepository);
assertNotNull(studyService);
Member member = new Member();
member.setId(1L);
member.setEmail("keesun@email.com");
Study study = new Study(10, "테스트");
given(memberService.findById(1L)).willReturn(Optional.of(member));
// When
studyService.createNewStudy(1L, study);
// Then
assertEquals(1L, study.getOwnerId());
then(memberService).should(times(1)).notify(study);
then(memberService).shouldHaveNoMoreInteractions();
}
2부. Mokito
1. Mockito 소개
2. Mockito 시작하기
3. Mock 객체 만들기
@Mock 애노테이션으로 만드는 방법
@Mock MemberService memberService;
@Mock StudyRepository studyRepository;
예시
구현체 없이 인터페이스만 가지고 Mock을 사용하지 않았을 때 테스트를 작성하면 아래와 같다.
4. Mock 객체 Stubbing
모든 Mock 객체의 행동
null
을 리턴한다. (Optional 타입은 Optional.empty 리턴)Mock 객체를 조작해서
예시
when()
을 가지고 mock 객체로stubbing
을 할 수 있다. (주석도 참고)member.setId(1L);
의 값이 달라지면when(memberService.findById(1L))
에서 아무것도 하지 않는다.그렇기 때문에 anyLong() 등을 사용할 수도 있다.
메소드가 동일한 매개변수로 여러 번 호출될 때 각기 다르게 행동하도록 조작할수도 있다.
세 번째 호출 : 빈 Optional 리턴
5. Mock 객체 Stubbing 연습문제
TODO
에 해당하는 작업을 코딩으로 채워 넣으세요.TODO
에 해당하는 작업6. Mock 객체 확인
예시
한 번만 호출되는지 확인 / 호출되지 않는지 확인
InOrder
로 순서대로 동작했는지 확인할 수 있고,verifyNoMoreInteractions()
로 더 이상 상호작용이 없는지 확인할 수도 있다.7. Mockito BDD 스타일 API
BDD: 애플리케이션이 어떻게 “행동”해야 하는지에 대한 공통된 이해를 구성하는 방법으로, TDD에서 창안했다.
행동에 대한 스팩
Mockito는 BddMockito라는 클래스를 통해 BDD 스타일의 API를 제공한다.
When -> Given
Verify -> Then
예시