버전

이 문서에서는 다음 버전을 사용하였다.

  • org.springframework.boot:spring-boot-starter-test:2.3.5.RELEASE
    • org.mockito:mockito-core:3.3.3
    • org.mockito:mockito-junit-jupiter:3.3.3
  • org.junit.jupiter:junit-jupiter-api:5.6.2

Examples

아래의 예제에 사용된 import는 모두 다음과 같다.

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;

예제용 클래스는 다음과 같다.

class Bird {
  void fly() {
    System.out.println("fly");
  }

  void walk() {
    System.out.println("walk");
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getName() {
    return this.name;
  }

  public String talk(String sentence) {
    return String.format("%s %s", this.name, sentence);
  }
}

verify 검증

verify

private final Bird bird = mock(Bird.class);

@DisplayName("'verify.메소드()' 호출은")
@Nested class Describe_verify {

  @DisplayName("메소드 호출이 있었다는 것을 검증한다")
  @Test void test1() {
    bird.fly();

    verify(bird).fly();
  }

  @DisplayName("never와 함께 사용하면 메소드 호출이 없었다는 것을 검증한다")
  @Test void test2() {
    bird.walk();

    verify(bird, never()).fly();
  }

  @DisplayName("times와 함께 사용하면 메소드 호출 횟수를 검증한다")
  @Test void test3() {
    for (int i = 0; i < 3; i++) {
      bird.fly();
    }

    verify(bird, times(3)).fly();
  }

  @DisplayName("atLeast, atMost와 함께 사용하면 메소드 호출 최소/최대 횟수를 검증한다")
    @Test void test4() {
      for (int i = 0; i < 3; i++) {
        mockingBird.fly();
      }

      verify(mockingBird, atLeast(2)).fly();
      verify(mockingBird, atMost(4)).fly();
    }
  }

  @DisplayName("argument를 제공하면 argument를 검증한다")
  @Test void test5() {
    mockingBird.setName("Kachi");

    verify(mockingBird).setName("Kachi");
    verify(mockingBird).setName(anyString());   // String 타입 검증
  }
}

verifyNoMoreInteractions

private final Bird bird = mock(Bird.class);

@DisplayName("verifyNoMoreInteractions는")
@Nested class Describe_verifyNoMoreInteractions {

  @DisplayName("verify 이후 다른 인터랙션이 없었다는 것을 검증한다")
  @Test void test4() {
    mockingBird.fly();

    verify(mockingBird).fly();
    verifyNoMoreInteractions(mockingBird);
  }
}

verifyNoInteractions

참고: verifyZeroInteractions는 3.0.1 부터 deprecated 되었으므로, verifyNoInteractions를 사용하도록 한다.

private final Bird bird = mock(Bird.class);

@DisplayName("verifyNoInteractions는")
@Nested class Describe_verifyNoInteractions {

  @DisplayName("아무런 인터랙션이 없었다는 것을 검증한다")
  @Test void test5() {

    verifyNoInteractions(mockingBird);
  }
}

inOrder

@DisplayName("inOrder는")
@Nested class Describe_inOrder{
  private final Bird mockingBird = mock(Bird.class);
  private final InOrder order = inOrder(mockingBird);   // 이 부분에 주목

  @DisplayName("메소드 호출 순서를 검증한다")
  @Test void test5() {
    mockingBird.fly();
    mockingBird.walk();
    mockingBird.fly();

    order.verify(mockingBird).fly();
    order.verify(mockingBird).walk();
    order.verify(mockingBird).fly();
  }
}

ArgumentCaptor의 사용

@DisplayName("ArgumentCaptor는")
@Nested class Describe_ArgumentCaptor {
  private final Bird mockingBird = mock(Bird.class);
  private final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);

  @DisplayName("argument를 캡처한다")
  @Test void test() {
    mockingBird.setName("Kachi");

    verify(mockingBird).setName(captor.capture());
    Assertions.assertEquals("Kachi", captor.getValue());
  }
}

when을 사용한 method stubbing

thenReturn, doReturn

private final Bird mockingBird = mock(Bird.class);

@DisplayName("thenReturn으로 리턴값을 지정할 수 있다")
@Test void test1() {
  mockingBird.setName("Kachi");
  when(mockingBird.getName()).thenReturn("dummy name");

  Assertions.assertEquals("dummy name", mockingBird.getName());
}

@DisplayName("thenReturn으로 리턴값을 순차적으로 지정할 수 있다")
@Test void test2() {
  mockingBird.setName("Kachi");
  when(mockingBird.getName())
      .thenReturn("dummy name")
      .thenReturn("false name")
      .thenThrow(new RuntimeException("3번 부르면 예외를 던집니다."));

  Assertions.assertEquals("dummy name", mockingBird.getName());
  Assertions.assertEquals("false name", mockingBird.getName());
  Assertions.assertThrows(RuntimeException.class, () -> mockingBird.getName());
}

@DisplayName("doReturn으로 리턴값을 지정할 수 있다")
@Test void test3() {
  mockingBird.setName("Kachi");
  doReturn("false name").when(mockingBird).getName(); // 이렇게도 가능하다

  Assertions.assertEquals("false name", mockingBird.getName());
}

thenThrow, doThrow

@DisplayName("thenThrow로 예외를 던지게 할 수 있다")
@Test void test4() {
  when(mockingBird.talk(anyString())).thenThrow(RuntimeException.class);

  Assertions.assertThrows(RuntimeException.class, () -> mockingBird.talk("hello"));
}

@DisplayName("doThrow로 예외를 던지게 할 수 있다")
@Test void test5() {
  // void를 리턴하는 메소드는 doThrow로 테스트할 수 있다.
  doThrow(RuntimeException.class).when(mockingBird).setName(anyString());

  Assertions.assertThrows(RuntimeException.class, () -> mockingBird.setName("Kachi"));
}

참고문헌