2020-12-5 백기선님 더자바 테스트(3) 강의정리 Mockito

Mockito

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

  • Mockito : Mock 객체를 쉽게 만들고 관리하고 검증할 수 있는 방법을 검증
  • Spring-boot-starter-test 의존성을 추가하면 Mockito가 자동으로 들어온다.
    • 없다면 의존성에 mockito-core, mockito-junit-jupiter를 추가하면 된다.
  • Mock을 만드는 방법, Mock이 어떻게 동작해야하는지 설정, Mock의 행동을 검증하는 방법 3가지를 통해 테스트를 쉽게 검증할 수 있다.

Mock 객체 만들기

  • 테스트할때 구현체는 없지만 의존성이 있는경우에 Mock을 만들기 좋다.

  • // Mock을 직접 만들기
    class StudyServiceTest {
    
      @Test
      void createStudyService() {
        MemberService memberService = mock(MemberService.class);
        StudyRepository studyRepository = mock(StudyRepository.class);
        StudyService studyService = new StudyService(memberService, studyRepository);
      }
    
    }
    
    // @Mock 어노테이션을 활용
    @ExtendWith(MockitoExtension.class)
    class StudyServiceTest {
    
      @Mock
      MemberService memberService;
    
      @Mock
      StudyRepository studyRepository;
    
      @Test
      void createStudyService() {
        StudyService studyService = new StudyService(memberService, studyRepository);
        assertNotNull(studyService);
      }
    }
    
    

// Mock을 특정 테스트에서만 쓰고 싶을때. @Test @DisplayName(“Mock을 특정 테스트에서만 사용하고 싶을때”) void createMockSpecificTest(@Mock MemberService memberService, @Mock StudyRepository studyRepository) { StudyService studyService = new StudyService(memberService, studyRepository); assertNotNull(studyService); }


  - @Mock을 사용하면 @ExtendWith을 이용하여 MociktoExtension.class를 등록해야한다.
  - 특정 테스트에서만 Mock을 쓰고 싶을때는 Mock객체를 테스트메서드 인자로 전달할 수 있다.

### Stubbing

- 모든 Mock객체

  - Null을 리턴
  - Primitive타입은 기본 Primitive
  - 컬렉션은 비어있는 컬렉션
  - void는 예외를 던지지 않고 아무일도 하지 않는다.

- Stubbing은 Mock객체가 특정 매개변수를 받아 특정값을 리턴하거나 예외를 던지도록할 수 있다.

-  ```java
  @Test
  void createStudyService() {
    StudyService studyService = new StudyService(memberService, studyRepository);
    Member member = new Member();
    member.setEmail("jimmy@email.com");
    member.setId(1L);
    when(memberService.findById(any())).thenReturn(Optional.of(member));

    Optional<Member> findMember = memberService.findById(1L);
    assertEquals("jimmy@email.com", findMember.get().getEmail());
    assertEquals("jimmy@email.com", memberService.findById(2L).get().getEmail());
    assertEquals(1L, findMember.get().getId());

    doThrow(new IllegalArgumentException()).when(memberService).validate(2L);
    assertThrows(IllegalArgumentException.class, () -> {
      memberService.validate(2L);
    });

    Study study = new Study(10, "java");
    studyService.createStudy(1L, study);
    assertNotNull(studyService);
  }

  @Test
  @DisplayName("예외에 대한 테스트")
  void exception_test() {
    StudyService studyService = new StudyService(memberService, studyRepository);
    Member member = new Member();
    member.setEmail("jimmy@email.com");
    member.setId(1L);
    // 첫번째 호출은 Optional<Member>, 두번째 호출은 RuntimeException, 세번째 호출은 Empty
    when(memberService.findById(any()))
      .thenReturn(Optional.of(member))
      .thenThrow(new RuntimeException())
      .thenReturn(Optional.empty());
    Optional<Member> findMember = memberService.findById(1L);
    assertEquals("jimmy@email.com", findMember.get().getEmail());
    assertThrows(RuntimeException.class, () -> {
      memberService.findById(1L);
    });
    assertEquals(Optional.empty(), memberService.findById(1L));
  }

  @Test
  @DisplayName("notify Test")
  void notifyTest(@Mock MemberService memberService, @Mock StudyRepository studyRepository) {
    StudyService studyService = new StudyService(memberService, studyRepository);
    assertNotNull(studyService);
    Member member = new Member(1L, "jimmy@email.com");
    Study study = new Study(10, "test");

    when(memberService.findById(1L)).thenReturn(Optional.of(member));
    when(studyRepository.save(study)).thenReturn(study);
    studyService.createStudy(1L, study);
    assertEquals(member, study.getOwner());
    verify(memberService, times(1)).notify(study);
  }
  • when()을 이용하여 특정 메서드의 특정 파라미터가 호출됬을때 특정 객체가 리턴되도록 설정할 수 있다.
  • any()는 ArgumentMatchers 클래스의 메서드로 anyObject(), anyBoolean(), matches() 등을 통해 다양한 파라미터를 받게할 수 있다.
Written on December 8, 2020