Notice
Recent Posts
Recent Comments
Link
관리 메뉴

look-forest

Mockito 본문

Mockito 소개

자바 테스트에서 사용하는 Mock 프레임워크.

 spring-boot-starter-test에서 자동으로 Mockito 추가해 줌

  • Mock: 진짜 객체와 비슷하게 동작하지만 프로그래머가 직접 그 객체의 행동을 관리하는 객체.
  • Mockito: Mock 객체를 쉽게 만들고 관리하고 검증할 수 있는 방법을 제공

 

 

단위 테스트에 대하여

단위 테스트에 대한 고찰

단위 테스트를 논할 때, '단위'의 기준은 정하기 나름이다. 클래스가 될 수도 있고, 기능이 될 수도 있다.

 

이미 존재하는 객체를 굳이 Mock으로 만들 필요는 없다고 생각한다.

다만, 내가 통제하기 힘든 외부 API 의 테스트 환경이 없다면 Mocking을 하는 수 밖에 없다.


Mock을 활용한 테스트

  1. Mock을 만드는 방법
  2. Mock이 어떻게 동작해야 하는지 관리하는 방법 (Stubbing)
  3. Mock의 행동을 검증하는 방법 (Verifying)

 

1. Mock 객체 만들기

  1. Mockito.mock() 메소드로 만드는 방법
  2. @Mock 애노테이션으로 만드는 방법
    • JUnit 5 extension으로 MockitoExtension을 사용해야 한다.
    • 필드 / 메소드 매개변수

※ 진짜 객체

  1. @InjectMocks : 테스트하려는 실제 객체(테스트 대상)에 @Mock 또는 @Spy로 선언된 객체들을 자동으로 주입.
  2. @Spy : 실제 클래스의 인스턴스를 생성하여 사용
  3. @MockitoBean : Spring Boot 테스트에서 Spring ApplicationContext에 등록된 실제 빈(bean)을 Mockito의 목(mock) 객체로 대체할 때 사용 (@Mock은 ApplicationContext에 자동으로 등록되지 않음)

※ MockitoExtension을 쓸 때는 Bean을 안붙여도 되는데, @SpringbootTest는 Bean을 붙이고 @Autowired를 쓴다.

@ExtendWith(MockitoExtension.class) //가짜 환경이라 Spring 관련 Bean들이 없는 환경
class UserServiceTest {

    //테스트 대상 클래스의 인스턴스를 생성하고, 해당 클래스의 필드에 적절한 목(Mock) 객체를 자동으로 주입
    @InjectMocks //모든 Mock들이 주입된다
    private UserService userService;

    @Mock //가짜 객체를 생성해 넣는다
    private UserRepository userRepository;

    @Spy //Spring IOC 에 있는 진짜 Bean 을 넣는다
    private PasswordEncoder passwordEncoder;
}

 

 

 

2. Mock 객체 Stubbing

메소드 스텁(method stub) :  다른 프로그래밍 기능을 대리하는 코드를 의미

 

모든 Mock 객체의 행동(default, stubbing 전)

  • Null을 리턴한다. (Optional 타입은 Optional.empty 리턴)
  • Primitive 타입은 기본 Primitive 값.
  • 콜렉션은 비어있는 콜렉션.
  • Void 메소드는 예외를 던지지 않고 아무런 일도 발생하지 않는다

 

Mock 객체를 조작하면

  • 특정한 매개변수를 받은 경우 특정한 값을 리턴하거나 예외를 던지도록 만들 수 있다.
  • 메소드가 동일한 매개변수로 여러번 호출될 때 각기 다르게 행동하도록 조작할 수도 있다.

 

Mockito.when(), Mockito.doThrow() 메소드를 통해 stubbing이 가능하다.

@Test
void stubbing(@Mock MemberService memberService) {
    Member member = new Member();
    member.setId(1L);
    member.setEmail("test@Test.com");

    //stubbing
    Mockito.when(memberService.findById(1L)).thenReturn(Optional.of(member));
    Mockito.doThrow(new IllegalArgumentException()).when(memberService).validate(1L);

    assertThat(member).isEqualTo(memberService.findById(1L).get());
    assertThrows(IllegalArgumentException.class, () -> memberService.validate(1L));

    //stubbing : 여러번 호출될 때 각기 다르게 행동하도록 지정
    Mockito.when(memberService.findById(any()))
            .thenReturn(Optional.of(member))
            .thenThrow(new IllegalArgumentException())
            .thenReturn(Optional.empty());

    assertEquals(member, memberService.findById(7L).get());
    assertThrows(IllegalArgumentException.class, () -> memberService.findById(1L));
    assertEquals(Optional.empty(), memberService.findById(1L));
}

 

 

3. Mock 객체의 행동을 검증하는 방법 (Verifying)

Mock 객체가 어떻게 사용이 됐는지 확인할 수 있다.

 

verify 메소드를 사용하면,

특정 메소드가 특정 매개 변수로 몇 번 호출 되었는지, 최소 한 번은 호출 됐는지, 전혀 호출되지 않았는지 등

@Test
void createNewStudy(@Mock MemberService memberService) {
    ...

    Mockito.verify(memberService, times(1)).notify(study);
//        verifyNoMoreInteractions(memberService);
    Mockito.verify(memberService, times(1)).notify(member);
    Mockito.verify(memberService, never()).validate(anyLong());

    //notify(study) 호출 후 notify(member) 호출 여부 확인
    InOrder inOrder = Mockito.inOrder(memberService);
    inOrder.verify(memberService).notify(study);
    inOrder.verify(memberService).notify(member);
}

BDD 스타일 Mockito API

BDD: Behavior Driven Development.

애플리케이션이 어떻게 “행동”해야 하는지에 대한 공통된 이해를 구성하는 방법으로, TDD에서 창안.

 

Mockito는 BddMockito라는 클래스를 통해 BDD 스타일의 API를 제공한다.

기존 api의 when, verify 등을 given / when / then 구조에 맞춰 명명하는 정도이다.

import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;

//Mockito BDD 스타일 API (Behavior Driven Development)
@Test
void BDD_Style(){
    // Given
    StudyService studyService = new StudyService(memberService, studyRepository);
    assertNotNull(studyService);

    Member member = new Member();
    member.setId(1L);
    member.setEmail("test@Test.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
    then(memberService).should(times(1)).notify(study);
    then(memberService).should(times(1)).notify(member);
    then(memberService).shouldHaveNoMoreInteractions();
}

참고 자료 & 이미지 출처
더 자바, 애플리케이션을 테스트하는 다양한 방법 (백기선 님)
Mockito 레퍼런스

 

'Test > 애플리케이션을 테스트하는 다양한 방법' 카테고리의 다른 글

Web MVC / Security / JPA 관련 test  (0) 2024.12.15
Junit 5  (2) 2024.11.10
테스트 - DB 연동  (0) 2024.08.17
테스트의 종류  (0) 2021.05.01