Skip to main content Link Menu Expand (external link) Document Search Copy Copied

created at 2024-02-11

Table of contents

  1. 용어 정리
    1. 테스트 대역(Test Double)
    2. Mock:
    3. Stub:
    4. Spy:

테스트 코드를 짜다보면 흔히 Mock, Stub, 테스트 더블 등등 여러가지 용어를 듣게 됩니다. 이번에는 이러한 용어들을 정리해보면서 비교해보겠습니다. 예시와 함께 말이죠!

본 포스팅에서 사용되는 용어와 그 의미, 예시들은 마틴 파울러씨의 Mocks Aren’t Stubs 를 참고하여 작성되었습니다!

용어 정리

테스트 대역(Test Double)

테스트 더블은 소프트웨어 테스트에서 실제 객체를 대신하여 사용되는 가상의 객체를 가리키는 일반적인 용어입니다. 그래서 테스트를 위한 실체 객체의 “대역” 이죠. 이러한 테스트 더블은 주로 의존성을 제거하거나 특정한 동작을 가상으로 시뮬레이션하기 위해 테스트 시에 사용됩니다. Test double 용어를 만든 Meszaros 는 stunt double(스턴트 대역) 에서 아이디어를 가져왔다고 합니다. 테스트 대역에는 몇 가지 대표적인 테스트 방법이 있습니다.

Mock:

Mock 은 사전에 기대치를 가지고 있어 호출되는 메서드에 대한 검증이 가능합니다. 테스트 메소드 내에서 when 으로 호출되는 메서드에 대한 반환값을 지정하고, verify 로 검증할 수 있답니다.

class OrderInteractionTester {
  public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    Mock warehouse = mock(Warehouse.class);
    Mock mailer = mock(MailService.class);
    order.setMailer((MailService) mailer.proxy());

    mailer.expects(once()).method("send");
    warehouse.expects(once()).method("hasInventory")
      .withAnyArguments()
      .will(returnValue(false));

    order.fill((Warehouse) warehouse.proxy());
  }
}

reference : https://martinfowler.com/articles/mocksArentStubs.html

Stub:

실제로 준비된 가짜 반환 값을 호출합니다. ex) StubUserRepository Overriding 테스트 중에 호출된 메서드에 대해 준비된 반환값을 제공합니다.

class OrderStateTester {
    public void testOrderSendsMailIfUnfilled() {
        Order order = new Order(TALISKER, 51);
        MailServiceStub mailer = new MailServiceStub();
        order.setMailer(mailer);
        order.fill(warehouse);
        assertEquals(1, mailer.numberSent());
}

Mock 과 비슷하죠? 하지만 다르답니다. 마틴 파울러씨가 말하길 Stub 은 상태 검증, Mock 은 행위 검증이라고 합니다.

In both cases I’m using a test double instead of the real mail service. There is a difference in that the stub uses state verification while the mock uses behavior verification.

In order to use state verification on the stub, I need to make some extra methods on the stub to help with verification. As a result the stub implements MailService but adds extra test methods.

Mock objects always use behavior verification, a stub can go either way. Meszaros refers to stubs that use behavior verification as a Test Spy. The difference is in how exactly the double runs and verifies and I’ll leave that for you to explore on your own.

reference : https://martinfowler.com/articles/mocksArentStubs.html

종합해보면 Mock 은 메소드 실행 횟수, 입력되는 파라미터 를 검증하고, Stub 은 반환값 을 검증합니다.

그래서 우리가 흔히 하는 아래 테스트의 경우, Mock & Stub 테스트를 하고 있는것이죠.

@ExtendWith(MockitoExtension.class)
class UserContextInterceptorTest {
    @Mock
    private JwtTokenValidator jwtTokenValidator;
    @Mock
    private UserRedisSessionRepository userRedisSessionRepository;
    @InjectMocks
    private UserContextInterceptor interceptor;
    @Test
    @DisplayName("preHandle 은 유효한 refresh token 이 쿠키에 있을 때 true 를 반환합니다")
    void testPreHandle_WithValidRefreshToken_ShouldReturnTrue() throws Exception {

        // given
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        request.setCookies(new Cookie("refreshToken", "valid_token"));
        request.setRequestURI("/some-path");
        request.setMethod("GET");

        // when
        when(jwtTokenValidator.validateToken("valid_token")).thenReturn("user123"); // Mock 객체에 반환 값을 정의하는 Stub
        boolean result = interceptor.preHandle(request, response, null);

        // then
        assertTrue(result); 
        assertThat(UserContext.getUserId()).isEqualTo("user123"); // 상태 검증
        verify(userRedisSessionRepository, never()).findById(anyString()); // 행위 검증
    }
}

Spy:

일부만 Stubbing(반환 값을 정의하는 Stub 과정) 되어 있는 실제 객체입니다. 이 Spy 는 실제 객체를 감시하거나 모니터링하기 위해 사용되는 객체로, 실제 객체의 특정 메서드 호출을 추적하고, 호출된 횟수를 세거나 호출된 인자를 기록하는 등의 작업을 수행합니다. 스파이는 객체의 상태나 동작을 확인하고 검증하는 데 사용됩니다.