Mock Object란 무엇인가?

Yoo Young-mo

--

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

이를 간단하게 코드로 나타내면 아래와 같다.

CellphoneMmsSender의 send() 메소드에 대한 테스트 코드를 작성 하려면 어떻게 해야 할까? 먼저 반환값을 검증하는 것을 고려할 수 있을 것이다. 하지만 테스트 대상인 send() 메소드의 반환 타입은 void 이다. 반환 값이 아니라면 무엇을 검증해야 할까?

CellphoneMmsSender 테스트 관점에서 중요한 것은 실제 문자 메시지를 보내는 것이 아니다. 실제 문자 메시지를 보내는 것은 CellphoneService의 책임이다. 그렇다면 CellphoneMmsSender send() 메소드에서 검증해야 하는 것은 전달 받은 메시지(msg)를 CellphoneService sendMMS()의 파라메터로 호출 했는지 여부이다.

CellphoneService의 sendMMS 호출 여부를 테스트 하기 위해서는 CellphoneMmsSender가 참조하고 있는 CellphoneService 객체를 가짜(대역) 객체로 대체하고 이를 검증하는 방법이 있다. 여기서의 사용하는 가짜 객체를 Mock Object 라고 한다.

Mock Object

Mock Object는 테스트 더블(Test Double)중 하나 이며 위키피디아에서는 아래와 같이 정의 한다.

In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts. — https://en.wikipedia.org/wiki/Mock_object

‘행위를 테스트 한다(test the behavior of some other object)’는 것은 무슨 의미일까? 위의 휴대 전화 문자 발신기 예제에서 ‘전달 받은 메시지(msg)를 CellphoneService sendMMS()의 파라메터로 호출 했는지 여부’가 행위에 해당하며 이를 테스트하는 것이다. 이를 행위 검증(Behavior Verification)이라고 한다.

Mock Object를 사용한 테스트 코드

직접 Mock Object를 만들어 테스트 코드를 작성해 보자. CellPhoneServiceMock은 CellPhoneService를 상속 받는다. 왜냐하면 실제 CellphoneMmsSender 객체에서 참조할 때 같은 유형의 객체이어야 동작하기 때문이다. sendMMS() 호출을 검증 하기 위한 추가적인 코드(isSendMMSCalled, getSendMsg)를 작성 하였다.

만들어진 Mock Object를 사용한 테스트 코드(JUnit 테스트 케이스)는 아래와 같다.

CellPhoneService 대신 Mock Object를 CellphoneMmsSender의 생성자로 주입(Injection) 하였으며, CellphoneMmsSender send() 수행 시 CellPhoneServiceMock은 호출 여부를 저장 한다. 마지막으로 JUnit Assert 를 사용하여 호출 여부와 예상한 메시지인지 검증 한다.

Mockito를 사용한 테스트 코드

Mock Object를 직접 만들어 테스트 코드를 작성할 수도 있지만, 일일이 클래스는 만들는 것은 번거럽기도 하지만 Mock 클래스 관리도 부담스럽다. 이를 지원하는 라이브러리나 프레임워크를 사용하는 것이 보다 편하고 상대적으로 시간도 절약할 수 있다. Java 진영에서 인기 있는 Mockito를 사용하여 테스트 코드를 작성 하였다.

Mockito에서 지원하는 mock을 사용하여 별도의 클래스를 만들지 않고 Mock Object를 만들었다. 이렇게 만들어진 Mock Object는 Mockito의 verify 를 통해 호출 여부를 검증 한다.

Stub과 Mock Object 무엇이 다른가?

There is a difference in that the stub uses state verification while the mock uses behavior verification.

출처 : http://martinfowler.com/articles/mocksArentStubs.html

마틴 파울러는 위의 글에서 Mock Object는 행위 검증(behavior verification)에 사용하고, Stub은 상태 검증(state verification)에 사용하는 것이라고 말하고 있다. 행위 검증은 이 글로 충분히 다루었으니, 상태 검증을 마틴 파울러 글의 메일 서비스 예시 코드를 살펴 보자.

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

위의 코드에서 상태(State)는 MailServiceStub의 messages 이다. 따라서 OrderStateTester은 MailServiceStub을 이용하여 상태(mailer.numberSent() — 메일 전송 횟수)를 검증(assertEquals) 한다. 결국 행위에 의해 만들어진 상태를 검증 하는 것이지 행위 자체를 검증 하고 있는 것은 아니다.

--

--