ข้ามไปยังเนื้อหา

การทดสอบด้วย pytest

ถ้าคุ้นกับ jest อยู่แล้ว โครงสร้างของ pytest จะคุ้นตาทันที แต่ syntax เรียบง่ายกว่าอย่างน่าพอใจ ไม่มี .toBe() ไม่มี wrapper expect() เพราะ pytest ใช้ assert ของ Python ตรง ๆ แล้ว rewrite ให้เบื้องหลัง เพื่อโชว์ diff ที่อ่านรู้เรื่องตอน test fail

รันในเทอร์มินัลของคุณ:

Terminal window
pip install pytest
# หรือ:
uv add --dev pytest
# รัน tests ทั้งหมด
pytest
# รันพร้อม verbose output
pytest -v
# รัน file เฉพาะ
pytest tests/test_math.py
# รัน test เฉพาะโดยชื่อ
pytest tests/test_math.py::test_add
TypeScript
// jest — TypeScript
import { add, greet } from './math';
describe('math utils', () => {
test('adds two numbers', () => {
expect(add(2, 3)).toBe(5);
});
test('greet returns a string', () => {
expect(greet('Alice')).toBe('Hello, Alice!');
});
test('throws on negative input', () => {
expect(() => add(-1, 0)).toThrow('negative');
});
});
Python
# pytest — Python
# ไฟล์ต้องขึ้นต้นด้วย test_ หรือลงท้ายด้วย _test.py
# Functions ต้องขึ้นต้นด้วย test_
from math_utils import add, greet
import pytest
def test_adds_two_numbers():
assert add(2, 3) == 5
def test_greet_returns_string():
assert greet("Alice") == "Hello, Alice!"
def test_raises_on_negative_input():
with pytest.raises(ValueError, match="negative"):
add(-1, 0)

pytest ค้นหา tests อัตโนมัติ — ไม่ต้อง config test runner เพื่อเริ่มต้น:

กฎpytestjest
Pattern ของไฟล์test_*.py หรือ *_test.py*.test.ts หรือ *.spec.ts
Pattern ของ functiontest_*test(...) หรือ it(...)
Pattern ของ classTest* (ไม่มี __init__)describe(...)
Setupsetup_method / fixturesbeforeEach
Teardownteardown_method / fixturesafterEach

ฝั่ง jest คุณใช้ beforeEach / afterEach ส่วน pytest ใช้ fixture ที่เป็น function ที่ inject เข้ามาเป็น dependency คอย setup resource และจะ teardown ต่อท้ายด้วยก็ได้ fixture ทำได้มากกว่า beforeEach เพราะ compose ต่อกันได้และกำหนด scope เองได้

TypeScript
// jest beforeEach
let db: MockDatabase;
beforeEach(() => {
db = new MockDatabase();
db.seed([{ id: 1, name: 'Alice' }]);
});
afterEach(() => {
db.close();
});
test('finds a user', () => {
expect(db.find(1)).toEqual({ id: 1, name: 'Alice' });
});
Python
# pytest fixture — เทียบกับ beforeEach/afterEach
import pytest
@pytest.fixture
def db():
database = MockDatabase()
database.seed([{"id": 1, "name": "Alice"}])
yield database # test รันที่นี่
database.close() # teardown หลัง yield
def test_finds_a_user(db): # pytest inject fixture
assert db.find(1) == {"id": 1, "name": "Alice"}
TypeScript
// jest — test.each สำหรับ data-driven tests
test.each([
[1, 2, 3],
[0, 0, 0],
[-1, 1, 0],
])('add(%i, %i) = %i', (a, b, expected) => {
expect(add(a, b)).toBe(expected);
});
Python
# pytest — @pytest.mark.parametrize
import pytest
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
])
def test_add(a, b, expected):
assert add(a, b) == expected
# pytest-style assertions ทำงานใน plain Python ด้วย
def add(a: int, b: int) -> int:
return a + b
def test_basic():
assert add(2, 3) == 5
assert add(0, 0) == 0
def test_string():
message = "Hello, World!"
assert "World" in message
assert message.startswith("Hello")
assert len(message) == 13
# รัน tests ด้วยตนเอง
test_basic()
test_string()
print("All assertions passed!")

Playground note: snippet ข้างต้นแสดง Python assert statements แบบธรรมดา pytest runner จริง (test discovery, fixtures, parametrize, --cov) ต้องการ terminal ที่ติดตั้ง pytest รัน pytest -v ใน project ของคุณเพื่อดูประสบการณ์จริง

โดยค่าเริ่มต้น pytest ค้นหา test functions ในไฟล์ที่ match pattern ใด?
อะไรคือคู่เทียบ pytest ของ pattern beforeEach / afterEach ของ jest?
เขียน assertion ใน pytest อย่างไร?
decorator ใดเปิดใช้ data-driven (parametrized) tests ใน pytest?