mock을 너무 많이 사용하면 테스트가 성공하더라도 실제 운영 환경에서는 코드가 작동하지 않는 '가짜 안도감'에 빠지게 됩니다. 테스트 대상의 내부 구현을 그대로 복제한 모킹은 작은 코드 수정에도 테스트가 깨지게 만들어 유지보수 비용을 기하급수적으로 높이는 주범이 됩니다.
우리가 테스트 코드를 작성하는 본질적인 이유는 코드 변경에 대한 자신감을 얻기 위해서입니다. 하지만 모든 의존성을 가짜 객체로 대체하는 순간, 테스트는 비즈니스 로직의 결과가 아니라 '메서드가 몇 번 호출되었는지'와 같은 지엽적인 구현 상세에 집착하게 됩니다. 이는 소프트웨어 테스트 전략의 전반적인 신뢰도를 떨어뜨리는 결과를 초래합니다.
효율적인 테스트 스위트를 구축하기 위해서는 무엇을 모킹하고 무엇을 실제 객체로 남겨야 하는지에 대한 명확한 기준이 필요합니다. 무분별한 격리보다는 실제 객체 간의 협력을 검증하는 방향으로 선회해야 테스트 본연의 가치를 살릴 수 있습니다.
이 글에서는 모킹이 과해질 때 발생하는 구체적인 문제점들을 짚어보고, 실무에서 바로 적용할 수 있는 테스트 설계 체크리스트와 대안을 살펴보겠습니다. 이를 통해 리팩토링이 두렵지 않은 단단한 테스트 코드를 만드는 법을 이해할 수 있을 것입니다.
핵심 내용 먼저 보기
핵심 키워드 mock 과다 사용 · 연관 검색어 mock 과다 사용, 테스트 설계 체크리스트, 모킹 부작용, 단위 테스트 전략, 행위 검증 상태 검증
구현 상세에 종속된 테스트가 리팩토링을 방해하는 과정
mock 과다 사용의 가장 큰 부작용은 테스트 코드가 실제 구현 코드의 '복사본'이 된다는 점입니다. 예를 들어, 특정 서비스가 내부적으로 A 메서드를 호출한 뒤 B 메서드를 호출한다는 사실을 일일이 verify()로 검증하고 있다면, 이는 로직의 결과가 아닌 과정을 테스트하는 것입니다. 결과적으로 로직의 결과물은 동일하더라도 내부 메서드 이름만 바꾸면 테스트가 실패하게 됩니다.
이런 현상이 반복되면 개발자는 테스트 코드를 '도움이 되는 도구'가 아니라 '수정을 방해하는 짐'으로 인식하게 됩니다. 리팩토링은 외부 동작을 유지한 채 내부 구조를 개선하는 작업인데, 모킹에 의존한 테스트는 내부 구조가 바뀔 때마다 함께 깨지기 때문에 리팩토링 자체를 불가능하게 만듭니다. 실무에서는 이를 '테스트의 취약성(Fragility)' 문제라고 부릅니다.
외부 의존성과 내부 로직을 구분하는 모킹의 적정선
모킹이 반드시 필요한 지점은 제어할 수 없는 외부 영역입니다. 데이터베이스, 외부 API 호출, 시스템 시간, 파일 시스템 등은 테스트 실행 환경에 따라 결과가 달라질 수 있으므로 모킹의 대상이 됩니다. 하지만 프로젝트 내부의 도메인 모델이나 유틸리티 클래스까지 모킹하는 것은 지양해야 합니다.
판단 기준은 간단합니다. 해당 객체가 '순수 로직'을 담고 있고 실행 속도가 빠르다면 실제 객체를 사용하는 것이 훨씬 이득입니다. 실제 객체를 사용하면 의존하는 클래스의 버그가 테스트 대상에게도 전파되어, 통합적인 관점에서 오류를 조기에 발견할 수 있습니다. 반면 모든 것을 모킹하면 각 부품은 정상이라고 나오는데 조립했을 때만 터지는 상황을 막을 수 없습니다.
Mock 대신 Fake 객체와 상태 기반 검증 활용하기
행위 검증(Behavior Verification) 위주의 모킹 대신 상태 검증(State Verification)과 Fake 객체를 활용해 보세요. Fake 객체는 실제 객체와 동일하게 동작하지만 훨씬 가벼운 구현체(예: 인메모리 리포지토리)를 의미합니다. 이를 사용하면 복잡한 when(...).thenReturn(...) 설정 없이도 실제 데이터의 흐름을 자연스럽게 테스트할 수 있습니다.
또한 메서드 호출 여부를 확인하기보다, 작업이 끝난 후 데이터의 상태가 어떻게 변했는지를 확인하는 것이 좋습니다. 상태 기반 검증은 내부 구현이 어떻게 바뀌든 최종 결과만 맞으면 통과하므로 테스트의 생명력이 훨씬 길어집니다. 실무에서는 복잡한 모킹 설정 코드가 테스트 본문보다 길어지고 있다면, 설계 자체가 잘못되었거나 모킹을 남용하고 있다는 신호로 받아들여야 합니다.
테스트 신뢰도를 높이기 위한 자가 진단 체크리스트
현재 작성된 테스트 코드가 건강한지 확인하려면 다음 세 가지를 점검해 보십시오. 첫째, 테스트 코드의 준비(Setup) 과정이 실행(Execute) 과정보다 압도적으로 긴가? 둘째, 비즈니스 로직과 상관없는 private 메서드나 내부 필드 접근을 위해 모킹을 사용하고 있는가? 셋째, 코드 한 줄을 고쳤을 뿐인데 관련 없는 수십 개의 테스트가 동시에 깨지는가?
만약 이 중 하나라도 해당한다면 모킹을 걷어내고 실제 객체를 사용하거나, 클래스를 더 작은 단위로 분리해야 할 때입니다. 테스트하기 어려운 코드는 대개 설계에 결함이 있다는 증거이기도 합니다. 모킹으로 억지로 테스트를 끼워 맞추기보다는, 테스트하기 쉬운 구조로 코드를 개선하는 것이 장기적인 생산성 측면에서 훨씬 유리합니다.
mock 과다 사용은 단기적으로는 테스트 작성 속도를 높여주는 것처럼 보이지만, 장기적으로는 프로젝트의 유연성을 갉아먹는 독이 됩니다. 테스트는 코드의 동작을 보장하는 안전망이어야지, 코드를 옥죄는 감옥이 되어서는 안 됩니다.
진정한 테스트의 가치는 리팩토링을 마음껏 할 수 있게 해주는 신뢰에서 나옵니다. 외부 시스템과의 경계선에서만 제한적으로 모킹을 사용하고, 내부 로직은 실제 객체 간의 협력을 통해 검증하는 습관을 들여보시기 바랍니다. 이는 자연스럽게 더 나은 객체지향 설계로 이어지는 긍정적인 피드백 루프를 만들어낼 것입니다.
이 글과 함께 읽어보면 좋은 주제로 '테스트 피라미드 전략의 실무 적용법'이나 '의존성 주입(DI)이 테스트 코드에 미치는 영향'에 대해 찾아보시는 것을 추천합니다. 테스트의 깊이를 더하고 싶다면 단위 테스트와 통합 테스트의 경계를 허무는 '사회적 테스트(Social Tests)' 개념을 공부해보는 것도 큰 도움이 됩니다.
자주 묻는 질문
모킹을 아예 안 쓰는 것이 가장 좋은 방법인가요?
아니요. 외부 API, 데이터베이스, 네트워크 통신처럼 제어 불가능하거나 실행 속도가 매우 느린 영역은 모킹이 필수적입니다. 핵심은 '제어 가능한 내부 로직'까지 모킹하지 않는 것입니다.
테스트 코드가 너무 길어지는데 모킹 때문일까요?
그럴 확률이 높습니다. 의존성이 많은 클래스를 테스트할 때 모든 의존성을 mock으로 설정(stubbing)하다 보면 셋업 코드가 비대해집니다. 이는 해당 클래스가 너무 많은 책임을 가지고 있다는 설계 결함의 신호일 수 있습니다.
실제 객체를 쓰면 단위 테스트가 아니지 않나요?
단위 테스트의 '단위'는 반드시 하나의 클래스일 필요가 없습니다. 하나의 동작(Behavior)을 단위로 본다면, 여러 객체가 협력하더라도 외부 의존성 없이 메모리 내에서 빠르게 실행된다면 훌륭한 단위 테스트입니다.
해시태그
#mock과다사용 #테스트설계체크리스트 #모킹부작용 #단위테스트전략 #행위검증상태검증 #테스트리팩토링
'IT' 카테고리의 다른 글
| 컨텍스트 윈도우, LLM이 한 번에 이해하는 정보량의 한계와 선택 기준 (0) | 2026.08.03 |
|---|---|
| 실서버 검증, 로컬 테스트가 완벽해도 배포 직후 장애가 발생하는 이유와 대응법 (0) | 2026.08.03 |
| 생성형 AI 보안, 기업 데이터 유출과 권한 남용을 막기 위해 검토해야 할 실무 기준 (0) | 2026.08.03 |
| AI 도입 체크리스트, 기술 선정보다 데이터와 조직 문화를 먼저 점검해야 하는 이유 (0) | 2026.08.02 |
| 기업 AI 도입, 기술 검토보다 '해결할 문제'의 우선순위부터 정해야 하는 이유 (1) | 2026.08.02 |