Mock 프레임워크(Mockito)의 핵심 기능 학습 테스트

주싱·2023년 2월 8일
0

더 나은 테스트

목록 보기
12/16

Java의 대표적인 Mock 프레임워크인 Mockito에서 제공하는 핵심 기능들에 대한 학습테스트 코드를 작성합니다.

1. 준비

우선 테스트를 위해 목킹 대상이 될 협력 클래스(Collaborator)와 협력에 사용할 메시지 클래스(Message)를 정의합니다.

// 협력 클래스
public class Collaborator {
    public int supply() {
        return 0;
    }

    public void consume(Message message) {
        System.out.printf("message id = %s, value = %d", message.getId(), message.getValue());
    }
}

// 메시지
@RequiredArgsConstructor
@EqualsAndHashCode
@Getter
public class Message {
    private final String id;
    private final int value;
}

그리고 학습 테스트 코드를 단순화 하기 위해 Mockito 클래스를 정적으로 import 하고, Collaborator 및 Captor 객체를 어노테이션을 통해 생성하도록 하겠습니다.

import static org.mockito.Mockito.*;

public class MockTest extends MockitoBaseTest {
    @Mock
    Collaborator collaborator;
    @Captor
    ArgumentCaptor<Message> argumentCaptor;
		...

Mockito에서 제공하는 어노테이션 기반 목 생성 기능을 사용하기 위해서는 아래와 같은 설정이 필요한데 역시 학습 테스트 코드를 단순히 하기 위해 Super 클래스로 분리해서 설정은 수행하도록 하겠습니다. 이제 주요 API 들을 학습하는 테스트 코드를 작성할 준비가 되었습니다.

public class MockitoBaseTest {
    private AutoCloseable autoCloseable;

    @BeforeEach
    void beforeEach() {
        autoCloseable = MockitoAnnotations.openMocks(this);
    }

    @AfterEach
    void afterEach() throws Exception {
        autoCloseable.close();
    }
}

2. Stub

목킹한 객체의 특정 메서드를 호출 할 때, 미리 정의한 특정한 값(또는 예외)을 반환하도록 설정할 수 있습니다.

값 반환

목 객체가 특정한 값을 반환하도록 설정합니다.

@Test
void mock_returns_specific_predefined_value() {
    // Given
    int fixed = collaborator.supply();
    Assertions.assertEquals(fixed, collaborator.supply());
    when(collaborator.supply()).thenReturn(fixed + 10);

    // When
    int actual = collaborator.supply();

    // Then
    Assertions.assertEquals(fixed + 10, actual);
}

예외 반환

목 객체가 특정한 예외를 반환하도록 설정합니다.

@Test
void mock_throws_specific_predefined_exception() {
    // Given
    when(collaborator.supply()).thenThrow(RuntimeException.class);

    // Expected
    Assertions.assertThrows(RuntimeException.class, collaborator::supply);
}

람다식을 통한 값 반환

목 객체가 람다식에 의한 계산 결과로서 특정 값을 반환하도록 설정할 수 있습니다.


@Test
void mock_returns_value_through_lambda_expression() {
    // Given
    when(collaborator.supply()).thenAnswer(invocation -> {
        // Do something
        return 123;
    });

    // When
    int actual = collaborator.supply();

    // Then
    Assertions.assertEquals(123, actual);
}

연속적인 반환 값/예외 구성

목 객체의 특정 메서드가 연속적으로 호출 될 때 각각의 반환 값(또는 예외)를 설정할 수 있습니다.


@Test
void mock_simulates_variety_stub_pattern() {
    // Given
    when(collaborator.supply())
            .thenThrow(RuntimeException.class)
            .thenReturn(10);

    // Expected
    Assertions.assertThrows(RuntimeException.class, collaborator::supply);
    Assertions.assertEquals(10, collaborator.supply());
}

3. Capture

목킹한 객체의 특정 메서드 호출 횟수, 호출 파라미터 값 등을 캡처하여 검증할 수 있습니다.

출력 타입 검증

목 객체의 특정 메서드를 호출할 때 전달된 파라미터 타입을 캡처하여 검증할 수 있습니다.

@Test
void mock_capture_output_type() {
    // When
    collaborator.consume(new Message("A", 1));

    // Then
    verify(collaborator, times(1)).consume(any(Message.class));
}

출력 메시지 검증

목 객체의 특정 메서드를 호출할 때 전달된 파라미터 값을 캡처하여 검증(equals 메서드 사용)할 수 있습니다.

@Test
void mock_capture_output_message() {
    // When
    Message message = new Message("A", 1);
    collaborator.consume(message);

    // Then
    verify(collaborator, times(1)).consume(message);
}

출력 메시지 목록 검증

목 객체의 특정 메서드가 연속적으로 호출될 때 전달된 파라미터 값 목록을 캡처하여 검증(equals 메서드 사용)할 수 있습니다.

@Test
void mock_capture_output_message_list() {
    // When
    Message message1 = new Message("A", 1);
    Message message2 = new Message("B", 2);
    collaborator.consume(message1);
    collaborator.consume(message2);

    // Then
    verify(collaborator, times(2)).consume(argumentCaptor.capture());
    List<Message> allOutputs = argumentCaptor.getAllValues();
    Assertions.assertEquals(allOutputs.get(0), message1);
    Assertions.assertEquals(allOutputs.get(1), message2);
}

타임아웃을 적용한 검증

목 객체의 특정 메서드가 특정 파라미터와 함께 호출되었는지 제한시간을 가지고 일정 시간 반복 검증할 수 있습니다.

@Test
void mock_verify_with_timeout() {
    // When
    Message message = new Message("A", 1);

    Executor delayedExecutor = CompletableFuture.delayedExecutor(1000, TimeUnit.MILLISECONDS);
    delayedExecutor.execute(() -> collaborator.consume(message));

    // Then
    verify(collaborator, timeout(10000).times(1)).consume(message);
}

4. 참고

profile
소프트웨어 엔지니어, 일상

0개의 댓글