최근에 테스트 코드를 짜기 시작하면서, Mock 객체를 가지고 어디서부터 어디까지 활용을 해야 하는지, 그리고 왜 Mock객체를 사용하는지를 모르고 얼레벌레 코드를 짜다 보니 목적성을 잃은 의미없는 테스트케이스를 짜게 되었다. 단순히 예상한 값을 나오게 하려고 테스트코드를 짜는 데에만 집중을 한 것이다.
그치만 테스트케이스를 만드는 것도 설계가 필요하고 테스트에 대한 명확한 의도가 필요하다.
검색해보니 테스트 코드를 위한 모의 객체들은 테스트 더블이라는 집합 아래 여러가지 방법론들이 있었다. 이 중에서 대표적으로 구분되는 Mock과 Stub에 대해 핵심 내용만 간략하게 공부해보고 정리해보았다.
테스트 더블이란?
테스트 더블은 '스턴드 터블'이라는 용어에서 아이디어를 얻어서 만들었다. 말 그대로 액터가 해야 할 행위를 대신하는 스턴트맨처럼 대체할 모의 객체를 생성해서 테스트를 진행하는 기술이라고 보면 된다.
- 테스트 대상 코드를 격리
- 테스트 속도 개선
- 예측 불가능한 실행 요소 제거
- 특수한 상황 시뮬레이션
테스트 더블에는 대표적으로 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
https://joont92.github.io/tdd/상태검증과-행위검증-stub과-mock-차이/
https://medium.com/@SlackBeck/mock-object란-무엇인가-85159754b2ac
'Dev > etc' 카테고리의 다른 글
키-값 저장소 설계 (0) | 2023.02.24 |
---|---|
스프링 빌드 시 Docker Container Image 생성 후 DockerHub push 자동화 하기 (0) | 2023.02.21 |
[Docker] Docker&Container 기본 개념 (0) | 2022.06.02 |
[Docker] Docker로 jar파일 이미지 빌드하기 (0) | 2022.02.19 |
[Docker] Docker 로 mariaDb 실행하고 접속하기 (0) | 2021.12.24 |
댓글