ป้ายกำกับ: Mocking

Unit Testing Frameworks: การเขียน Unit Test และ Mocking ด้วย PyTest และ unittest.mock สำหรับ PythonUnit Testing Frameworks: การเขียน Unit Test และ Mocking ด้วย PyTest และ unittest.mock สำหรับ Python

ในโลกของการพัฒนาซอฟต์แวร์ที่ระบบมีความซับซ้อนและมีการเชื่อมต่อกับฐานข้อมูลภายนอกอย่าง PostgreSQL อย่างหลีกเลี่ยงไม่ได้ การทดสอบโค้ด (Testing) จึงไม่ใช่ทางเลือก แต่เป็นรากฐานสำคัญที่ช่วยให้มั่นใจว่า Logic ทางธุรกิจของเราทำงานได้อย่างถูกต้องและเสถียร แม้ว่าเราจะมีการเปลี่ยนแปลงฟีเจอร์ใหม่ ๆ ก็ตาม

Unit Testing และความท้าทายในการเชื่อมต่อฐานข้อมูล

Unit Testing คือการทดสอบส่วนประกอบที่เล็กที่สุดของโค้ดอย่างโดดเดี่ยว (Isolation) เมื่อโค้ดของเราต้องมีการเรียกใช้ฐานข้อมูลจริง (เช่น การเชื่อมต่อผ่าน `psycopg2` เพื่อรันคำสั่ง SQL กับ PostgreSQL) การทดสอบเหล่านั้นจะทำให้ Unit Test ของเราช้ามาก ไม่เสถียร และอาจเกิดข้อผิดพลาดจากสภาพแวดล้อมภายนอกได้

นี่คือจุดที่เทคนิค **Mocking** เข้ามามีบทบาทสำคัญ Mocking คือการจำลอง (Simulate) การทำงานของส่วนประกอบภายนอก เช่น การจำลองการเชื่อมต่อฐานข้อมูล หรือการเรียก API ภายนอก แทนที่จะเชื่อมต่อกับ PostgreSQL จริง ๆ เราจะใช้ไลบรารี unittest.mock เพื่อสร้างวัตถุจำลอง (Mock Object) ขึ้นมา

การใช้ PyTest และ Mocking กับ PostgreSQL

PyTest เป็นเฟรมเวิร์กที่ได้รับความนิยมสูงสุดใน Python เนื่องจากใช้งานง่ายและมีฟีเจอร์ที่ช่วยให้การเขียน Test Case มีประสิทธิภาพสูง เมื่อเราต้องการทดสอบฟังก์ชันที่ต้องดึงข้อมูลจาก PostgreSQL เราไม่ควรให้มันเชื่อมต่อกับ DB จริง แต่ควร Mock การเชื่อมต่อทั้งหมด

ตัวอย่างเช่น หากเรามีฟังก์ชันที่รับรายการ ID และต้องดึงข้อมูลผู้ใช้จากตาราง `users` ใน PostgreSQL เราจะ Mock การเรียกใช้ `cursor.execute()` เพื่อให้ฟังก์ชันของเราคิดว่ามันได้รับผลลัพธ์ที่สมบูรณ์แบบ โดยไม่ต้องให้มันไปรันคำสั่ง SQL กับเซิร์ฟเวอร์จริง ๆ นี่คือหัวใจของการทำ Unit Test ที่รวดเร็วและเชื่อถือได้

Python

import pytest
from unittest.mock import MagicMock
from my_service import get_user_details # สมมติว่าฟังก์ชันนี้ใช้ DB Connection

def test_get_user_details_success(mock_db_connection):
    # 1. ตั้งค่า Mock Object ให้จำลองการทำงานของ DB Connection
    mock_cursor = mock_db_connection.cursor
    
    # 2. กำหนดให้ mock_cursor.execute() ส่งคืนผลลัพธ์ที่ต้องการ
    mock_cursor.execute.return_value = [('1', 'John Doe', 'Admin')]
    
    # 3. เรียกฟังก์ชันที่กำลังทดสอบ (ซึ่งจะใช้ mock_db_connection แทนการเชื่อมต่อจริง)
    result = get_user_details(mock_db_connection, user_id='1')
    
    # 4. ตรวจสอบว่าฟังก์ชันทำงานถูกต้องตามที่คาดหวัง
    assert result['name'] == 'John Doe'
    
    # 5. ตรวจสอบว่ามีการเรียกใช้คำสั่ง SQL ที่ถูกต้อง (PostgreSQL Query)
    mock_cursor.execute.assert_called_with("SELECT id, name, role FROM users WHERE id = %s", (user_id,))

บทสรุป: สร้างระบบที่มั่นคงด้วยการทดสอบ

การรวมพลังของ PyTest และ `unittest.mock` ทำให้เราสามารถแยก Logic ทางธุรกิจออกจาก Infrastructure Layer (เช่น PostgreSQL Connection) ได้อย่างสมบูรณ์ ทำให้ Unit Test ของเรามีความเร็วสูง (Fast) และความน่าเชื่อถือสูง (Reliable) ในฐานะ Senior Architect การใช้เทคนิคนี้เป็นสิ่งจำเป็นอย่างยิ่งในการรักษาคุณภาพของโค้ดฐานข้อมูลขนาดใหญ่ เพื่อให้มั่นใจว่าทุกการเปลี่ยนแปลงฟีเจอร์ใหม่จะไม่ส่งผลกระทบต่อส่วนที่เคยทำงานได้ดีอยู่แล้ว