Notes & software courses · Free to learn
Aph's Blog
On this page

Mock & แยก dependency

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

เทสต์โค้ดที่พึ่งของภายนอก (API, DB, เวลา) โดยใช้ของปลอมแทน เพื่อให้เทสต์เร็วและเสถียร

เทสต์ที่ดีต้องเร็วและให้ผลเหมือนเดิมทุกครั้ง แต่ถ้าโค้ดไปยิง API จริงหรือแตะ DB จริง เทสต์จะช้าและไม่เสถียร (เน็ตหลุด, ข้อมูลเปลี่ยน) การ mock คือแทนของภายนอกด้วย "ของปลอม" ที่เราคุมได้

ทำไมต้อง mock

  • เร็ว — ไม่ต้องรอ network จริง
  • เสถียร — ผลไม่ขึ้นกับเซิร์ฟเวอร์/ข้อมูลภายนอก
  • ทดสอบเคสยาก — จำลอง error เช่น API ล่ม, timeout ได้
  • ไม่กระทบของจริง — ไม่เผลอลบข้อมูล/ส่งอีเมลจริงตอนเทสต์

mock การเรียก API

สมมติฟังก์ชันเราเรียก requests.get ไปดึงข้อมูล เราไม่อยากให้เทสต์ยิงเน็ตจริง จึงแทน requests.get ด้วย mock ที่คืนค่าที่เรากำหนด

python
# code.py
import requests

def get_user_name(user_id):
    resp = requests.get(f"https://api.example.com/users/{user_id}")
    return resp.json()["name"]
python
# test_code.py
from unittest.mock import patch
from code import get_user_name

@patch("code.requests.get")
def test_get_user_name(mock_get):
    # กำหนดให้ของปลอมคืนค่าที่เราต้องการ
    mock_get.return_value.json.return_value = {"name": "Aph"}

    assert get_user_name(1) == "Aph"   # ไม่ยิงเน็ตจริง
    mock_get.assert_called_once()      # ตรวจว่าถูกเรียกจริง

monkeypatch — fixture ของ pytest

pytest มี fixture ชื่อ monkeypatch ใช้แทนค่า/ฟังก์ชันชั่วคราวระหว่างเทสต์ได้ง่าย เช่นแทนค่าเวลาหรือ env var

python
def get_mode():
    import os
    return os.environ.get("MODE", "dev")

def test_get_mode(monkeypatch):
    monkeypatch.setenv("MODE", "prod")   # ตั้ง env ชั่วคราว
    assert get_mode() == "prod"
    # พ้นเทสต์ค่าคืนเดิมอัตโนมัติ
เทสต์ที่ยิงเน็ตจริงคือเทสต์ที่ไม่ดี

เทสต์ที่ต้องต่อ network/DB จริงจะช้า ล้มเมื่อเน็ตหลุด และอาจกระทบข้อมูลจริง — แยก dependency ภายนอกออกด้วย mock ให้ unit test รันเดี่ยว ๆ เร็วและเสถียร (เก็บการต่อของจริงไว้ที่ integration test จำนวนน้อย)

สรุปหัวข้อนี้

  • mock = แทนของภายนอก (API/DB/เวลา) ด้วยของปลอมที่คุมได้
  • ทำให้เทสต์เร็ว เสถียร จำลอง error ได้ ไม่กระทบของจริง
  • unittest.mock.patch แทนฟังก์ชัน + กำหนด return_value
  • monkeypatch (pytest) แทนค่า/env ชั่วคราวในเทสต์
แบบฝึกหัด

1) เขียนฟังก์ชันที่เรียก requests.get แล้ว mock ไม่ให้ยิงเน็ตจริง 2) ใช้ assert_called_once ตรวจว่าถูกเรียก 3) ใช้ monkeypatch.setenv แทน env var ในเทสต์ 4) อธิบายว่าทำไมไม่ควรให้ unit test ยิง API จริง