본문 바로가기
Dev/etc

행위검증 vs 상태검증 (Mock vs Stub)

by ssyoni 2022. 7. 3.
반응형

 

최근에 테스트 코드를 짜기 시작하면서, Mock 객체를 가지고 어디서부터 어디까지 활용을 해야 하는지, 그리고 왜 Mock객체를 사용하는지를 모르고 얼레벌레 코드를 짜다 보니 목적성을 잃은 의미없는 테스트케이스를 짜게 되었다. 단순히 예상한 값을 나오게 하려고 테스트코드를 짜는 데에만 집중을 한 것이다. 

그치만 테스트케이스를 만드는 것도 설계가 필요하고 테스트에 대한 명확한 의도가 필요하다. 

검색해보니 테스트 코드를 위한 모의 객체들은 테스트 더블이라는 집합 아래 여러가지 방법론들이 있었다. 이 중에서 대표적으로 구분되는 Mock과 Stub에 대해  핵심 내용만 간략하게 공부해보고 정리해보았다. 


테스트 더블이란?

출처 : https://tecoble.techcourse.co.kr/post/2020-09-19-what-is-test-double/

테스트 더블은 '스턴드 터블'이라는 용어에서 아이디어를 얻어서 만들었다. 말 그대로 액터가 해야 할 행위를 대신하는 스턴트맨처럼 대체할 모의 객체를 생성해서 테스트를 진행하는 기술이라고 보면 된다. 

  • 테스트 대상 코드를 격리
  • 테스트 속도 개선
  • 예측 불가능한 실행 요소 제거
  • 특수한 상황 시뮬레이션

테스트 더블에는 대표적으로 Stub, Mock 외에 Dummy, Spy, Fake 등이 있지만 각각의 기술들이 명확하게 역할을 나누어 가지는 것이 아니기 때문에 대표적으로 Stub과 Mock으로 구분할 수 있다. 

특히 Stub과 Mock은 혼돈하기 쉬운 경향이 있기 때문에 이 둘의 차이점에 대해서 간단하게 정리해보고자 한다.

 

 

상태 검증 vs 행위 검증 

stub과 mock은 상태검증이냐 행위검증이냐에 따라 구분될 수 있다. 

상태검증이란?

: 메서드가 수행될 때 연관되어있는 협력 객체의 '상태'를 검증함으로써 제대로 기능이 동작하고 있는지를 검증하는 것이다. 

행위 검증이란?

: 테스트하고자 하는 메소드가 참조하고 있는 협력 객체의 메소드를 제대로 콜 하는지에 대한 '행위'를 검증하는 것이다.

 

 

 

stub vs mock 

stub 

  • 더미 객체를 생성하고 실제로 동작하는것처럼 보이게 만든 가짜 객체이다. 
  • 호출된 요청에 대한 응답값을 미리 만들어놓고 전달한다. 
  • 객체의 최소한의 기능만을 임의로 구현한다. 

mock

  • 특정 동작을 수행하는지(= 메소드를 제대로 콜 하는지)에 대한 검증을 한다.
  • 즉 행위검증을 추구한다는 점에서 다른 테스트 더블들과 구분된다. 

직관적으로 요약을 해보자면 stub=상태 검증, mock=행위 검증이라고 볼 수 있겠다. 

클린 코드 저자인 마틴 파울러의 예시를 보자.

 

다음 어떠한 조건일 경우 고객에게 메일을 보내는 메일링 시스템이 있다고 가정한다.

테스트 조건 : 메일링 기능을 테스트하되 고객에게 실제로 메일 메세지를 보내진 않는다. 

테스트 방법 : 제어하고 조작할 수 있는 이메일 시스템의 테스트 더블을 생성한다. 

stub을 사용한 테스트

public interface MailService {
  public void send (Message msg);
}
public class MailServiceStub implements MailService {
  private List<Message> messages = new ArrayList<Message>();
  public void send (Message msg) {
    messages.add(msg);
  }
  public int numberSent() {
    return messages.size();
  }
}

 

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());
  }

올바른 상태값이 나오는지를 체크한다. 

위의 케이스 같은 경우는 테스트를 위해서 MailServiceStub이라는 추가적은 메소드를 생성했다. 

 

mock을 사용한 테스트

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());
  }
}

상태값을 비교하진 않지만 mock object를 사용해서 메소드 호출 여부 등을 간단하게 검증한다. 

 

=> 즉 stub과 mock은 실제 서비스 대신 테스트 더블을 사용하고 있지만, stub은 상태 검증을 하고 mock은 행위 검증을 하고 있다는 점에서 차이가 있다. 

 

 

 

 

 

https://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs

 

Mocks Aren't Stubs

Explaining the difference between Mock Objects and Stubs (together with other forms of Test Double). Also the difference between classical and mockist styles of unit testing.

martinfowler.com

https://joont92.github.io/tdd/상태검증과-행위검증-stub과-mock-차이/

 

[tdd] 상태검증과 행위검증, stub과 mock 차이

SUT(System Under Test) : 주요 객체(primary object) 협력객체(collaborator) : 부차적 객체(secondary objects) 테스트 더블(Test Double) : 테스팅을 목적으로 진짜 객체대신 사용되는 모든 종류의 위장 객체 Dummy, Fake Ob

joont92.github.io

https://medium.com/@SlackBeck/mock-object란-무엇인가-85159754b2ac

 

Mock Object란 무엇인가?

다른 누군가로부터 휴대 전화 서비스(CellphoneService) 기능을 제공 받아 이를 사용한 휴대 전화 문자 발신기(CellphoneMmsSender)를 프로그래밍 한다고 생각해 보자.

medium.com

 

반응형

댓글