본문 바로가기
Programming/Django

🧪 Python Mock 완벽 가이드 — 테스트에서 가짜 객체를 쓰는 이유

by Mandy's 2025. 8. 5.

1. Mock이란?

Mock은 테스트에서 진짜 객체 대신 쓰는 가짜 객체입니다.
주로 외부 의존성이 큰 코드(DB, 네트워크, 파일, 시간 지연 등)를 대체하여,
테스트를 빠르고, 안정적으로, 상황에 맞게 실행할 수 있게 해줍니다.

“Mock은 테스트의 세계에서 배우로 등장하는 대역 배우입니다.”


2. 왜 Mock을 쓰나?

  • 빠른 테스트: DB 연결, API 호출, 대기 시간 없이 실행
  • 상황 통제: 예외 발생, 특정 응답 값 등 원하는 상황 재현 가능
  • 행동 검증: 어떤 함수가 몇 번, 어떤 인자로 호출됐는지 확인 가능

3. Python에서 Mock 도구

Python 표준 라이브러리 unittest.mock이 제공하는 주요 기능:

  • Mock / MagicMock → 자유롭게 속성과 메서드를 정의할 수 있는 기본 가짜 객체
  • patch → “사용되는 곳”의 객체를 일시적으로 Mock으로 교체
  • return_value → 호출 시 반환할 값 지정
  • side_effect → 호출 시 예외 발생, 호출별 다른 값 지정 등
  • 검증 메서드 → assert_called_once(), assert_called_with(...), call_count 등
  • spec / autospec → 실제 객체 인터페이스에 맞춰 Mock 생성

4. 간단 예제

from unittest.mock import Mock, patch
import time

# 1) 기본 Mock 사용
api = Mock()
api.fetch.return_value = {"ok": True}

print(api.fetch())  # {'ok': True}
api.fetch.assert_called_once()

# 2) patch 사용
@patch("time.sleep")
def test_retry(mock_sleep):
    # sleep을 실제로 기다리지 않게 함
    mock_sleep.return_value = None

    # 여기서 retry 함수 호출...

    mock_sleep.assert_called()  # 호출 여부 확인

5. Django 테스트 예시

다음 예시는 Django 커맨드 wait_for_db가 DB 준비까지 대기하는 기능을 테스트하는 코드입니다.

from unittest.mock import patch
from psycopg2 import OperationalError as Psycopg2Error
from django.core.management import call_command
from django.db.utils import OperationalError
from django.test import SimpleTestCase

@patch('core.management.commands.wait_for_db.Command.check')
class CommandTest(SimpleTestCase):
    def test_wait_for_db_ready(self, patched_check):
        patched_check.return_value = True
        call_command('wait_for_db')
        patched_check.assert_called_once_with(database=['default'])

    @patch('time.sleep')
    def test_wait_for_db_delay(self, patched_sleep, patched_check):
        patched_check.side_effect = [Psycopg2Error]*2 + [OperationalError]*3 + [True]
        call_command('wait_for_db')
        self.assertEqual(patched_check.call_count, 6)

여기서 Mock이 하는 일

  1. @patch('core.management.commands.wait_for_db.Command.check')
    → 실제 Command.check 메서드를 Mock으로 교체해 DB 점검을 흉내냅니다.
  2. patched_check.return_value = True
    → DB가 바로 준비된 것처럼 시뮬레이션.
  3. patched_check.side_effect = [...]
    → 처음 5번 호출은 예외 발생, 6번째에서 True 반환 → 재시도 로직 검증.
  4. @patch('time.sleep')
    → 대기 시간을 실제로 기다리지 않도록 함.

6. Mock과 유사 개념

  • Stub: 고정된 값만 반환하는 단순 대역
  • Fake: 간단한 동작 구현을 가진 가짜(예: 메모리 DB)
  • Spy: 실제 함수를 호출하면서 호출 정보를 기록(Mock의 변형)

7. 실전 팁

  • “정의된 곳”이 아니라 “사용되는 곳” 경로로 patch하세요.
  • autospec=True 옵션을 사용하면 실제 함수/메서드 시그니처를 강제해 오타 방지.
  • 외부 시스템, 느린 연산, 불확정 동작(API 응답 랜덤 등)에 적극적으로 Mock 사용.

8. 마무리

Mock은 단순히 “가짜 객체”가 아니라, 테스트를 제어하고 검증하는 핵심 도구입니다.
잘 활용하면 테스트 속도를 크게 높이고, 예외 상황까지 안정적으로 커버할 수 있습니다.


💡 관련 문서