인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

홀리몰리님의 프로필 이미지

작성한 질문수

Practical Testing: 실용적인 테스트 가이드

Mock & Stub 에 대한 이해

작성

·

60

·

수정됨

0

두 가지 궁금증이 생겨 질문드립니다!!

  • Mock과 Stub에서 혼란을 느끼는지?

  • Mock & Stub 에 대한 이해를 하였는지?

     

     

// OrderTest.java

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class OrderTest {

    @Test
    void 주문_총_금액을_계산한다() {
        Product apple = new Product("사과", 1000);
        Product banana = new Product("바나나", 2000);

        Order order = new Order();
        order.addProduct(apple);
        order.addProduct(banana);

        assertEquals(3000, order.totalPrice());
    }
}
// OrderTest.java

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

public class OrderTest {

    @Test
    void 주문_총_금액을_계산한다_Product_Mock_사용() {
   
        Product apple = mock(Product.class);
        Product banana = mock(Product.class);

        when(apple.getPrice()).thenReturn(1000);
        when(banana.getPrice()).thenReturn(2000);

        Order order = new Order();
        order.addProduct(apple);
        order.addProduct(banana);

  
        assertEquals(3000, order.totalPrice());
    }
}

 

두 테스트에서 생각을 정리하다 보니 저는 마틴 파울러의 test double의 stub 과 mock 에 대한 개념보다

TTD or BDD 스타일의 테스트 코드를 작성하다가 테스트의 편의성을 위해 Mokito을 통한 Mock 테스트를 먼저 접하게 된 후 마틴 파울러의 test double의 개념을 접하게 되었습니다.

 

마틴 파울러의 Mock 과 Stub 테스트에서 혼란스러운 이유

테스트 코드를 작성하다보면 자연스럽게 then에 해당하는 부분은 대부분 상태(값)을 검증하는 테스트를 위주로 테스트 코드를 작성하게 되는거 같습니다.

  • 주문_총_금액을_계산한다() -> 단위 테스트로 상태(값)을 검증

  • 주문_총_금액을_계산한다_Product_Mock_사용() -> 나는 Mockito의 mock 테스트상태(값)을 검증

     

test double

  • stub -> 상태(값) 검증

  • mock -> 행동 검증

위 와 같이 상태(값)을 검증하는 테스트를 하다보니 stub 테스트Mockito 의 mock을 사용한 테스트을 동일시 보게 된거같습니다.

Mockito의 mock 테스트의 개념과 test double의 mock 테스트가 동일한 개념이 아니다. Mokito의 mock을 사용하여 상태(값)을 검증하는 테스트를 하다보니 test double의 stub 테스트Mock 테스트 하였다라고 생각한게 아닌가?

 

테스트 케이스 stub 과 mock 테스트 구별하기

  • 주문_총_금액을_계산한다()

    • 해당 테스트는 실제 객체를 이용하여 상태(값)에 대한 검증을 한다. -> Stub 테스트

  • 주문_총_금액을_계산한다_Product_Mock_사용()

    • 해당 테스트는 Product 를 Mock 하여, order의 totalPrice 을 검증 한다. -> Mock?, Stub?

       

       

     

주문_총_금액을_계산한다_Product_Mock_사용()를 어떻게 바라 볼 것인가?

  • test double의 mock 테스트를 준비 하였지만 검증 부분을 잘못하였다.

     

  • test double의 stub 테스트를 실제 객체가아닌 가짜객체로 테스트 하였다.

@Test
void 주문_총_금액을_계산한다_Product_Mock_사용() {
    // given
    Product apple = mock(Product.class);
    Product banana = mock(Product.class);

    when(apple.getPrice()).thenReturn(1000);
    when(banana.getPrice()).thenReturn(2000);

    Order order = new Order();
    order.addProduct(apple);
    order.addProduct(banana);

    // when
    int total = order.totalPrice();

    // then
    verify(apple).getPrice();   // apple.getPrice()가 호출되었는지 검증
    verify(banana).getPrice(); // banana.getPrice()가 호출되었는지 검증
}
  • 주문_총_금액을_계산한다_Product_Mock_사용()를 위와 같이 수정한다면 test double에서 이야기하는 mock 테스트이지 않을까 생각이 듭니다.

 

물론 모든 테스트를 test double의 Mock, Stub 테스트라는 틀에 맞춰 작성해한다는 아니라고 생각이 듭니다.

주문_총_금액을_계산한다_Product_Mock_사용()가 마틴 파울러가 이야기하는 test double의 개념에서 어떻게 생각해 볼 것인가? 에대한 궁금증 우빈님의 생각이 궁금하기도 합니다.

 

제가 잘 이해를 하였는지 확인이 필요하여 질문드립니다.

 

 

 

답변 2

0

박우빈님의 프로필 이미지
박우빈
지식공유자

안녕하세요, 홀리몰리 님!

충분히 가질 수 있는 고민이라는 생각이 드네요.

Mockito의 mock 객체를 사용해서, 상태 검증만 한다면 stub 테스트, 행위 검증까지 한다면 mock 테스트라고 볼 수 있겠지만, mock 객체를 사용해서 stub 테스트를 했다고 잘못 되었다고 보지는 않습니다.

이미 인지하고 계시듯이 보통 개발자들끼리 소통할 때도 mock과 stub을 엄밀하게 구분하면서 이야기하지는 않고, mock이라는 용어로 포괄적으로 사용하는 것을 볼 수 있는데요.
(stubbing이라는 용어보다는 mocking이라는 용어가 더 친숙한 것처럼요.)

개념적으로만 이해하시면 되고, stub이든 mock이든 검증하고자 하는 방향성에 맞게 사용하시면 될 것 같아요.

도움이 되셨기를 바랍니다.
감사합니다 🙂

0

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다. 홀리몰리님이 Mock과 Stub의 개념에 대해 혼란스러워하는 부분에 대해 이해하고자 질문하셨군요.

1. Mock과 Stub의 차이:

  • Stub: 특정 입력에 대해 미리 정의된 결과를 반환하는 객체입니다. 주로 검증하려는 로직이 아닌 대상의 동작에 의존하는 부분을 대체할 때 사용됩니다. Stub은 결과를 예측할 수 있고, 상태를 검증하는 데 유용합니다.

  • Mock: 객체의 동작에 대한 기대치를 설정하고, 기대했던 방식으로 객체가 호출되는지를 검증하는 것입니다. Mock은 주로 객체 간의 상호작용을 테스트하고자 할 때 사용됩니다.

2. 홀리몰리님의 테스트 코드에 대한 분석:

  • 주문_총_금액을_계산한다():

    • 실제 Product 객체를 사용하여 Order의 총 금액을 검증합니다. 이는 Stub 테스트로 간주될 수 있습니다, 왜냐하면 상태(값)의 변화를 검증하기 때문입니다.
  • 주문_총_금액을_계산한다_Product_Mock_사용():

    • 여기서 Product를 Mock으로 대체하여 Order의 총 금액을 검증합니다. Mock 객체를 사용하지만 기대되는 행위(getPrice 메서드 호출)를 검증하지 않는다면 사실상 Stub처럼 사용되는 것입니다.
    • 그러나 마지막에 verify 구문을 추가하여 getPrice()는 사실상 Mock의 행동을 검증함으로써 Mocking의 특징을 살릴 수 있습니다. 동작 검증까지 포함하여 테스트를 더욱 명확하게 만들 수 있습니다.

추가적인 도움이 되는 링크들:

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.