티스토리 뷰

 

TDD를 학습하며

"Stub과 Mock을 구분하고, 레이어를 적절히 나누어 테스트 방법론을 적용해야 함"을 배웠다..

 

 

Stub..?
네이버 사전

 

 

테스트 더블의 분류에서도  스텁(Stub)이 나온다.

인프런 강의 전 들었던 세미나에서도 스텁을 설명했다.

 

여태까지 들어온 "Stub"이라는 용어들이 충돌했다.

Stub을 중심으로 이 모든 용어를 이해 보자.

 


 

Mock 테스트의 Stubbing

 

실용적 테스트 가이드 강의를 들으며 '매출을 메일로 보내는 기능을 테스트했다'

그림의 파란색 박스가 테스트하는 부분이다. (상위 컨트롤러는 생략)

 

1) 특정 날짜를 조회하여 매출 메일을 요청하면,

2) OrderService가 조회일의 주문 총합을 구하고

3) 계산된 매출액을 MailSendClient에게 전달하며 메일 요청을 한다.

4) 외부 메일 서비스를 이용해 메일을 보내고, 히스토리를 저장한다.

5) 사용자(테스트)는 히스토리를 검증해서 메일 성공을 확인할 수 있다.

 

 

아래와 같이 JUnit과 Mockito를 활용해 테스트 코드를 작성하고,

메일 성공 응답을 stubbing 했다.

 

@DisplayName("특정 날짜로 결제완료 주문들을 조회하여 매출 통계 메일을 전송한다.")
   @Test
   void sendOrderMail() {
     // given
     // 결제 완료 주문 정보를 생성/저장한다.
 
     // stubbing
     Mockito.when(mailSendClient.sendMail(anyString(), anyString(), anyString())
         .thenReturn(true);
 
     // when
     boolean result = orderService.sendOrderMail(LocalDate.of(2025,3,27), "manager@test.com");
 
     // then
     assertThat(result).isTrue();
 
     List<MailSendHistory> histories = mailSendHistoryRepository.findAll();
     assertThat(histories).hasSize(1)
         .extracting("content")
         .contains("검색 일자의 매출 합계는 3000원입니다.")
     ;
   }

 

 

아주 이해하기 쉬운 Mockito 덕분에, 테스트 코드에서 스터빙을 이해하기는 쉽다.

"어떤 값이 오든! 해당 메서드의 응답은 true를 반환해라!"

 

테스트를 위해서 매번 메일 사용비를 지출할 수는 없다.

마치 메일 API처럼 있지만, 그냥 지정된 응답값만 반환하는 가짜 메일 오브젝트가 생겼다.

 

 

 

외부 서비스의 응답값을 스터빙한 덕분에,

목적이었던 sendOrderMail 메서드에 집중할 수 있다.

 

이제 Mockito를 활용한 테스트에서 말하는 Stub을 정리해 볼 수 있겠다.

 

 

Stub : 지정된 상황에 고정된 응답값을 반환하는 가짜 객체 또는 그런 행위

 

 


 

 

내가 처음 들어본 Stub

 

정보처리기사를 공부해 봤다면 아래 내용을 알고 있을 것이다. (정보처리기사는 흥달쌤!)

V model

통합 테스트는 상향식, 하향식, 빅뱅, 백본 테스트로 나눈다.
테스트 장치 도구(Test Harness)로는 드라이버, 스텁, 목 오브젝트.. 등이 있다.
- 상향식 테스트 (Bottom-up) : 상위 모듈이 미개발되었다면 '드라이버'를 사용한다.
- 하향식 테스트 (Top-down) : 하위 모듈이 미개발되었다면 '스텁'을 사용한다.

 

 

테스트 코드에서 사용하는 방식을 보면

결국 가짜 객체를 말하는 걸 텐데..

 

 

그럼 드라이버는 뭐지..?

 

What is the difference between stubs and drivers in software testing? (원본링크)


Stubs
- Definition: A stub is a simplified implementation of a module or component that a module under test depends on. It simulates the behavior of the missing component by providing predetermined responses to calls made during the test.
Drivers
- Definition: A driver is a program or component that invokes a module or component under test. It simulates the higher-level calling environment, providing input and controlling the execution flow.
(...)

 

https://www.quora.com/What-is-the-difference-between-stubs-and-drivers-in-software-testing

 

간단히 말해서

결국 사용하는 위치에 따라 명칭이 달라진다.

상위 모듈과 하위 모듈을 흐름에서 하는 역할이 다르다.

 

내가 테스트하려는 모듈의 상위 모듈은 나의 테스트의 트리거가 될 수 있다.

반대로 나의 하위 모듈은 메일 API 같은 경우일 수 있다.

 

테스트 하네스 스텁과 드라이버의 차이

 

 

 

여기까지 읽었다면, 이제 Stub을 좀 알게 되었다.

 

Stubs and drivers both are dummy modules and are only created for test purposes.

 


 

Mock이 뭐예요?

 

Stub이랑 조금 친해졌다.

하지만 테스트를 이해하려면, Mock과 Stub을 구분해야 한다.

Mock은 뭘까?

 

iphone mock-up image (https://mockuptree.com/free/hand-holding-iphone-16-pro-mockup/)

 

목업(Mock-up)이라는 단어를 들어본 적 있나?

목업 제품, 목업 이미지 등등.. 디자인 파일을 만져봤다면 들어봤을 것이다.

실제와 비슷한 시제품, 가짜 모형을 뜻한다.

 

결국 Mock도 테스트를 위한 가짜 객체를 의미한다.

 

좀 전에 '드라이버와 스텁의 차이'에서 이런 인용구가 있었다.

 

Stubs and drivers both are dummy modules and are only created for test purposes.

테스트를 위해서 생성한 가짜 객체!

 

xUnit Test Patterns 저자, 제라드 메스자로스(Gerard Meszaros)은

이러한 가짜 모듈에 대해서 Test Double이라고 정리했다.

 


 

Test Double

 

출처 https://people.com/movies/actors-and-their-stunt-doubles-photos/

 

테스트 더블은 스턴트 더블(Stunt double)에서 따왔다.

한국에서 많이 부르는 스턴트맨이라는 표현을 넘어,

배우와 외모, 체형마저 닮아 위험한 액션 외에 간단한 연기도 하는 이들을 스턴트 더블이라고 부른다.

(참고, 슬랭처럼 그냥 Mock이라고 말하는 것도 Test Double과 동일한 의미를 가지기도 한다.)

 

위에서 본 것처럼 배우인 MailClient를 대신해서 연기하는 가짜 MailClient를 칭한다.

우리는 위험한 테스트를 위해서 가짜 객체를 사용할 것이다. (표현하자면)

 

언제 어떻게 사용하느냐에 따라 테스트 더블의 명칭이 달라진다.

 


출처 http://xunitpatterns.com/Test%20Double.html

테스트 더블 (Test Double)
- 더미 (Dummy) : 아무런 동작도 하지 않는 인스턴스이다.
- 스텁 (Stub) : 테스트에 맞도록 의도한 응답을 반환한다.
- 스파이 (Spy) : Stub과 유사하지만 스스로 호출된 내역을 기록한다.
- 목 (Mock) : Stub과 유사하지만 예상 값으로 프로그래밍되어있다. Stub과 달리 예외가 발생할 수 있다.
- 페이크 (Fake) : 실제 동작하는 객체이지만 제품에는 사용할 수 없는 임시 객체이다.

 

 

Dummy

말 그대로 가짜이다. 아무런 기능을 할 수 없고 파라미터를 채우기 위한 용도가 될 수 있다.

 

Stub

의도한 응답을 반환하도록 단순하게 처리된 인스턴스이다.

테스트의 목적대로 동작하기 때문에 예외가 발생할 수 없다.

주로 테스트 중인 클래스에 데이터를 제공하기 위해 사용한다.

 

Spy

Stub과 매우 유사하게 동작한다. 단 호출될 때마다 호출 내역을 기록한다. (마치 잠입한 감시자처럼)

void 반환을 가진 기능을 검사한다면 활용할 수 있다.

(**Mockito내에 Spy 어노테이션이 있다. 이것은 테스트 더블의 스파이와는 다른 것이다!)

 

Mock

상호작용을 검사하기 위한 일종의 Stub이다. 자신이 할 기능까지 알고 있다는 점에서 Spy보다 발전된 임시 객체다.

Mock은 인스턴스 연관관계에 따라서 응답하기 때문에 예외가 발생할 수 있다.

테스트 중이 클래스에 의해 호출되고 동작 결과를 확인할 수 있다.

데이터와 구현 모두를 제공한다는 부분이 Stub과의 차이이다. 

 

Fake

이것은 Stub이 아니다! 진짜 실제 동작하는 객체이다. 하지만 제품에는 사용할 수 없다.

시뮬레이션과 같이 진짜 세상에 나가기 전 다른 가상 환경을 검사하는 것으로 비유할 수 있다.

 

 

출처 https://www.youtube.com/watch?v=4q9d8Aye0nY&t=2169s

 

 

백명섭님 강의에서 이 부분을 잘 설명해 주신다.

테스트를 위해 지정된 데이터를 처리 임시 객체임은 Stub하위로 비슷하다.

 

하지만 Mock은 지정된 데이터를 활용하여 구현한 것이기 때문에

결과에 따라 테스트가 실패할 수 있다.

 

Fake는 결국 가짜 객체가 아니이기 때문에 Stub과 동일한 구조를 가지지 않는다.

 

 

 

 

 

 

 

어떤 걸 언제 사용해야 할까?

 

 


 

 

Test Double의 사용 : Classical Test vs Mockist Test

출처 http://xunitpatterns.com/Test%20Double.html

 

객체지향의 원리에 따라 설계했다면, 테스트되는 주컴포넌트는 상대가 가짜인지 진짜인지 모른다. 알 필요도 없다.

 

위 그림에서 SUT(system under test)는 우리가 테스트하려는 객체이고, DOC(depended-on component)는 테스트에 따라 상호작용하는 다른 객체를 의미한다.

 

DOC의 역할을 테스트 더블이 대신한다. SUT는 신경 쓰지 않고 코드를 따라서 동작한다.

테스트 더블이 어떤 영향력을 가지는지(Stub인지 Mock)인지에 따라 테스트 케이스의 테스트 범위가 달라진다.

 

 

가장 처음 보았던 주문-메일 테스트를 다시 확인해 보자.

외부 API를 연동한 서비스 부분을 Mock처리해 버렸다. 외부 세계의 응답은 우리가 아무리 서비스를 잘 짜더라도,

어느 날 그 메일서비스가 터지면 어쩔 수 없다.

외부 메일 서비스를 스터빙 처리하는 것은 괜찮은 방법 같다.

 

 

근데,, 어차피 OrderService를 테스트하는 거라면,

MailSendClient가 메일 전송 요청을 잘 보냈는지 알 필요가 있을까?

그림처럼 MailSendCLient의 응답을 스터빙해 버려도,

MailSendClient에게 매출 합계 전송이 잘 되었는지는 확인할 수 있으니 OrderService를 테스트한 것은 맞다.

 

가능한 많은 것을 테스트 더블로 만들어버리고, 내가 원하던 주문 서비스에만 집중할 수도 있다.

하지만 통합 테스트에서 주문 서비스는 실패할 수도 있다.

 

So should I be a classicist or a mockist?

마틴 파울러의 Mocks Aren't Stubs 인용

Claasical Test를 활용해 온 마틴 파울러는 Mockist의 필요성을 인지하지 못했다. (인지할 필요를 못 느낌)

하지만 Mockist Test 방식은 디버깅 시간을 줄여주며 TDD를 도입하는 속도를 올려줄 수 있다. 동시에 프로그래머에게 즐거움을 줄 수도 있겠다.

 

정답은 없고, 나에게 맞는 것을 찾아보기를 바라는 것으로 글은 끝이 났다.

 

 

나의 생각 마무리

 

 

The purpose of this article was, and is, to point out these differences and to lay out the trade-offs between them.

마틴 파울러도 위 글의 목적은 두 문제의 영향력을 이해하는 것이라고 했다.

 

기술은 아는 것보다 습득하는 것이 남는다. 직접 다양한 상황에 적용해 보며 트레이드오프 결과를 체감해야 한다.

경험을 바탕으로 어떤 방식의 테스트 패턴을 적용할지는 그 상황에 맞춰서 할 수 있는 선택지를 정해야 할 것 같다.

 

 


참고 사이트

 

 

Practical Testing: 실용적인 테스트 가이드 강의 | 박우빈 - 인프런

박우빈 | , 실무에 맞는 올바른 테스트 코드 그 첫걸음이 되어드릴게요!  [사진] [임베딩 영상] 테스트 코드가 정말 그렇게 중요한가요? 🤔 그럼요! 테스트 코드 없이는 내가 만든 애플리케이션

www.inflearn.com

 

Test Double at XUnitPatterns.com

Test Double The book has now been published and the content of this chapter has likely changed substanstially. Please see page 522 of xUnit Test Patterns for the latest information. Also known as: Imposter How can we verify logic independently when code it

xunitpatterns.com

 

bliki: Test Double

Test Double is generic term for fakes, mocks, stubs, dummies and spies.

martinfowler.com

 

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://www.youtube.com/watch?v=4q9d8Aye0nY&t=2169s

https://jonghoonpark.com/2023/11/01/%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%9D%98-%EB%91%90-%EB%B6%84%ED%8C%8C

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

 

'Test' 카테고리의 다른 글

테스트 주도 개발(TDD)과 계층 단위 테스트  (0) 2025.03.25
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함