WEB/Java Test

Mockito

Tony Lim 2022. 11. 11. 10:48
728x90

Mock = 진짜 객체와 비슷하게 동작하지만 프로그래머가 직접 그 객체의 행동을 관리하는 객체

Mockito = Mock 객체를 쉽게 만들고 관리하고 검증할 수 있는 방법을 제공, 많이 쓰임

 

Mock 생성

public StudyService(MemberService memberService, StudyRepository repository) {
    assert memberService != null;
    assert repository != null;
    this.memberService = memberService;
    this.repository = repository;
}

test에서 StudyService를 만들어서 써야하지만 MemberService, StudyRepository는 interface만 존재하는 상황이다. 이런 상황에서 mocking을 사용할 수 있다.

StudyRepository studyRepository = mock(StudyRepository.class);

Mockito.mock 메소드로 직접 만드는 방법이 존재하고 

@ExtendWith(MockitoExtension.class)
class StudyServiceTest {
    @Test
    void createStudyService(@Mock MemberService memberService,
                            @Mock StudyRepository studyRepository) {
        StudyService studyService = new StudyService(memberService, studyRepository);
        assertNotNull(studyService);
    }
}

확장팩 MockitoExtension 을 써야 @Mock 했을때 null이아니라 제대로 mock 객체를 만들어준다.

필드에 선언하는 경우는 여러 테스트에서 쓰는 경우에 쓰인다. 위 예시는 해당 메소드에서만 쓰이기 때문에 인자로 줌


Mock 객체 Stubbing = Mock 객체의 행동을 조작하는것

Mock객체는 default로

1. Null 을 리턴한다. (Optional 타입의 경우 Optional.empty() 로 나온다)
2. Primitive타입은 Privmitive 타입을 리턴
3.콜렉션은 비어있는 콜렉션
4.Void 메소드는 에외를 던지지 않고 아무런 일도 발생하지 않음 -> 그냥 지나감

    @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("keesun@email.com");

        when(memberService.findById(1L))
                .thenReturn(Optional.of(member));

        Study study = new Study(10, "java");
        Optional<Member> findById = memberService.findById(1L);
        assertEquals("keesun@email.com",findById.get().getEmail());

mock 객체인 memberservice에서 해야할 행동을 when 에서 정의할 수 있다. 이때 인자로 1L이 들어오는 것만 정의 하였기 때문에 아래에서 사용할떄 2L 같은 다른 인자를 주게되면 기본 값인 Optional.empty() 가 튀어나올 것이다.

인자로 들어오는것과 매칭되는것만 정의되는 개념이 Arguement matcher이다.

when(memberService.findById(any()))
        .thenReturn(Optional.of(member))
        .thenThrow(new RuntimeException())
        .thenReturn(Optional.empty());

stubbing consecutive calls 의 예시이다. 3번 호출 되면 차례대로 명시한것들이 튀어나오게 된다.


 mock 객체 확인

public Study createNewStudy(Long memberId, Study study) {
    Optional<Member> member = memberService.findById(memberId);
    study.setOwner(member.orElseThrow(() -> new IllegalArgumentException("Member doesn't exist for id: '" + memberId + "'")));
    Study newstudy = repository.save(study);
    memberService.notify(newstudy);
    memberService.notify(member.get());
    return newstudy;
}
studyService.createNewStudy(1L, study);

assertEquals(member, study.getOwner());

verify(memberService, times(1)).notify(study);
verify(memberService,never()).validate(any());
verifyNoMoreInteractions(memberService);

InOrder inOrder = inOrder(memberService);
inOrder.verify(memberService).notify(member);
inOrder.verify(memberService).notify(study);

테스트코드에서 mock 객체인 studyService#createNewStudy 메소드 안에있는 notify 가 1번 호출이 되었는지 확인 할 수 있다.
createNewStudy 안의 notify를 주석처리 하면 테스트가 실패하게 된다.

verifyNoMoreInteraction의 경우 아래의 inorder.notify 때문에 테스트가 실패하게 된다.

메소드의 호출 순서를 verify 할 수있다. inOrder 메소드를 통해서 차례대로 호출이 되고 있는지 확인하게 된다.


BDD = Behavitor Driven Development 

행동에 대한 스펙을 정의한다.

@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));
    given(studyRepository.save(study)).willReturn(study);

    // When
    studyService.createNewStudy(1L, study);

    // Then
    assertEquals(member, study.getOwner());
    then(memberService).should(times(1)).notify(study);
    then(memberService).shouldHaveNoMoreInteractions();
}

given -> when -> then 의 논리 template을 적용한다.

 

 

728x90

'WEB > Java Test' 카테고리의 다른 글

운영 이슈 + 아키텍처 테스트  (0) 2022.11.14
TestContainers  (1) 2022.11.11
JUnit5  (0) 2022.11.10