테스트 스텁(Test Stub)이란 무엇인가?

Yoo Young-mo
7 min readApr 26, 2016

--

지난 글 ‘단위 테스트 케이스와 테스트 더블(Test Double)’ 에서 테스트 더블에 대해 이야기 했다. 이 글은 지난 글에 이어 테스트 더블의 하나인 테스트 스텁(Stub)에 대해 이야기 한다.

위키피디아에서는 테스트 스텁을 아래와 같이 정의 한다.

Test stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test.

출처 : https://en.wikipedia.org/wiki/Test_stub

여기서 중요한 것은 스텁은 ‘canned answer’를 호출한 쪽에 제공(provide) 한다는 것이다. 여기서 ‘canned answer’를 무엇일까?

인터뷰 상황을 예를 들어 보자. 질문자가 인터뷰 하기 전에 인터뷰 대상자에게 사전에 질문지를 주어 답을 미리 준비할 수 있게 하고 인터뷰 질의 과정에서 인터뷰 대상자는 사전에 준비한 답을 하는 경우가 있다. ‘정해진 질문에 대해 사전에 준비한 답’ 즉 ‘canned answer’ 이다.

그렇다면 테스트 스텁은 언제 사용할까?

쇼핑몰에서 고객이 주문한 상품(Order)을 결제 하기 위해 외부 결제 시스템(Payment Gateway)을 연동해야 한다고 가정해 보자.

Order는 Payment Gateway에 결제 요청 하며 Payment Gateway는 결제 요청에 대한 결과를 응답 한다.

Order를 격리된 단위 테스트 케이스를 작성한다고 하면 의존하는 Payment Gateway를 대체하여 미리 만들어진 응답 결과(canned answer)를 반환하게 해야 한다.

우리는 테스트 스텁을 Payment Gateway를 대체 하기 위해 사용할 수 있다.

코드 예시

프로그래밍 언어는 요즘 가장 보편적인 언어인 JavaScript를 선택 하였다. 실무에서 이렇게 코드를 작성하지 않겠지만 스텁을 이해하는 관점에서 간단한 코드를 작성해 보자.

// order.js
var Order = (function () {
// 생성자 함수
function Order(productName, amount) {
this.productName = productName;
this.amount = amount;
}

Order.prototype.pay = function () {
// 결제 요청
var pg = new PaymentGateway()
var paymentResult = pg.requestPayment(this.productName, this.amount);
if(paymentResult == ‘OK’) {
// 결제 성공
// …
// …
return true;
} else {
//결제 실패
// …
// …
return false;
}
};

return Order;
}());

var PaymentGateway = (function () {
// …
function PaymentGateway() {
// …
}

PaymentGateway.prototype.requestPayment = function () {
// …
// …
};

return PaymentGateway;
}());

Order는 PaymentGateway의 requestPayment를 호출하여 결제 요청을 한다. 결제가 성공하면 true를 반환하고 실패하면 false를 반환한다.

위의 코드에서 PaymentGateway를 스텁 처리하여 Order의 단위 테스트 케이스를 작성해 보자.

스텁을 직접 구현할 수도 있지만 대부분의 경우 스텁을 자동적으로 만들어주는 라이브러리를 사용한다. 이 글에서는 JavaScript 라이브러리 중에서 스텁을 지원하는 Sinon.JS를 사용 한다.

<!-- order_test.html -->
<html>
<head>
<meta charset=”utf-8" />
<title>Order Test</title>
</head>
<body>
<h1>Order Test</h1>
<script type=”text/javascript” src=”sinon-1.17.3.js”></script>
<script type=”text/javascript” src=”order.js”></script>
<script type=”text/javascript”>
(function () {
console.log(“== 테스트 케이스 — 결제가 성공한 경우”);

// Given
// Sinon 을 사용하여 Stub 처리
var stub = sinon.stub(PaymentGateway.prototype, ‘requestPayment’);
// canned answer
stub.withArgs(‘테스트 상품’, 2000).returns(‘OK’);

// When
var order = new Order(‘테스트 상품’, 2000);
var result = order.pay();

// Then
if(result == true) {
console.log(“> 테스트 성공”);
} else {
console.log(“> 테스트 실패”);
}

stub.restore();
})();

(function () {
console.log(“== 테스트 케이스 — 결제가 실패한 경우”);

// Given
// Sinon 을 사용하여 Stub 처리
var stub = sinon.stub(PaymentGateway.prototype, ‘requestPayment’);
// canned answer
stub.withArgs(‘테스트 상품’, 0).returns(‘ERROR’);

// When
var order = new Order(‘테스트 상품’, 0);
var result = order.pay();

// Then
if(result == false) {
console.log(“> 테스트 성공”);
} else {
console.log(“> 테스트 실패”);
}

stub.restore();
})();

</script>
</body>
</html>

위의 코드에서 두개의 테스트 케이스를 만들었으며 Sinon.JS를 사용하여 PaymentGateway.requestPayment 스텁 처리 하였다. 테스트 케이스를 명확하게 작성하기 위해 GivenWhenThen 패턴을 따라 작성 하였다.

실행 결과는 아래와 같다.

테스트 스텁을 사용하여 얻을 수 있는 이점

  • 의존하는 것에 독립적으로 개발/테스트가 가능 하다.

Payment Gateway가 아직 개발 되지 않을 수 있다. 서로간의 인터페이스만 합의되면 스텁으로 대체하여 개발하고 테스트할 수 있다.

  • 촘촘한 테스트가 가능 하다.

스텁으로 다양한 응답 결과(canned answer) 케이스(결제 성공/실패/예외)를 만들어 테스트할 수 있다.

--

--