pytest — การทดสอบ Python สมัยใหม่
pytest คือมาตรฐานโดยพฤตินัยสำหรับการทดสอบโค้ด Python ต่างจากโมดูล unittest ที่มีอยู่ในตัว pytest ไม่ต้องการการสืบทอดคลาส ไม่ต้องการเมธอด assertion พิเศษ และไม่มี boilerplate — คุณเขียนแค่ฟังก์ชันธรรมดาและคำสั่ง assert ธรรมดา ระบบค้นหาเทส ผลลัพธ์เมื่อเกิดความล้มเหลวที่ละเอียด ระบบ fixture และระบบนิเวศปลั๊กอินทำให้ pytest เป็นเครื่องมือแรกที่ควรหยิบใช้ในทุกโปรเจกต์ Python
การเขียนฟังก์ชันทดสอบ
หัวข้อที่มีชื่อว่า “การเขียนฟังก์ชันทดสอบ”pytest ค้นหาไฟล์ทดสอบที่มีชื่อขึ้นต้นด้วย test_ หรือลงท้ายด้วย _test.py และภายในนั้น ฟังก์ชันที่มีชื่อขึ้นต้นด้วย test_ ไม่จำเป็นต้องมีคลาส — ฟังก์ชันระดับโมดูลก็เพียงพอ
def add(a: int, b: int) -> int: return a + b
def divide(a: float, b: float) -> float: if b == 0: raise ValueError("Cannot divide by zero") return a / bfrom math_utils import add, divideimport pytest
def test_add_positive(): assert add(2, 3) == 5
def test_add_negative(): assert add(-1, -2) == -3
def test_divide_normal(): assert divide(10, 2) == 5.0
def test_divide_by_zero(): with pytest.raises(ValueError, match="Cannot divide by zero"): divide(1, 0)การรันการทดสอบ
หัวข้อที่มีชื่อว่า “การรันการทดสอบ”# run all testspython -m pytest
# verbose outputpython -m pytest -v
# run only tests matching a keywordpython -m pytest -k "divide"Fixtures
หัวข้อที่มีชื่อว่า “Fixtures”Fixture คือฟังก์ชัน setup (และ teardown) ที่นำกลับมาใช้ซ้ำได้ pytest จะส่งผ่านเข้าสู่ฟังก์ชันทดสอบตามชื่อพารามิเตอร์ — ไม่ต้องมีคลาสฐาน ไม่ต้องมีเมธอด setUp ใช้ yield เพื่อแยกส่วน setup ออกจาก teardown
import pytest
@pytest.fixturedef sample_data(): """Provide a list of numbers for testing.""" return [1, 2, 3, 4, 5]
def test_sum(sample_data): assert sum(sample_data) == 15
def test_length(sample_data): assert len(sample_data) == 5
@pytest.fixturedef db_connection(): """Setup and teardown a fake DB connection.""" conn = {"connected": True} # imagine a real DB here yield conn conn["connected"] = False # teardown runs after test
def test_db(db_connection): assert db_connection["connected"] is TrueParametrize
หัวข้อที่มีชื่อว่า “Parametrize”@pytest.mark.parametrize รันฟังก์ชันทดสอบเดียวกันหลายครั้งด้วยชุดข้อมูล input ที่แตกต่างกัน ทำให้ test suite กระชับและเพิ่ม edge case ได้ง่าย
import pytestfrom math_utils import add
@pytest.mark.parametrize("a, b, expected", [ (1, 2, 3), (0, 0, 0), (-1, 1, 0), (100, -50, 50),])def test_add_parametrized(a, b, expected): assert add(a, b) == expectedconftest.py — shared fixtures
หัวข้อที่มีชื่อว่า “conftest.py — shared fixtures”การวาง fixture ไว้ในไฟล์ conftest.py ทำให้ fixture นั้นใช้ได้กับไฟล์ทดสอบทั้งหมดในไดเรกทอรีเดียวกันและไดเรกทอรีย่อย โดยไม่ต้อง import เพิ่มเติม
# conftest.py (place in project root or test dir)import pytest
@pytest.fixture(scope="session")def app_config(): return {"debug": True, "db_url": "sqlite:///:memory:"}