จัดโครงสร้างการทดสอบที่แข็งแรงด้วย pytest: การฉีด fixture, teardown ที่อิงกับ yield, การพารามิเตอร์ไรซ์แบบตาราง และกับดักของ scope ที่กว้างเกินไป
เปิดบทเรียนนี้ใน Kodokonpytest ตั้งอยู่บนแนวคิดที่ทรงพลังสองข้อ การค้นหาตามข้อตกลง (ไฟล์ test_*.py และฟังก์ชัน test_*) และ assert ธรรมดา ๆ ซึ่งการทำ introspection ของมันจะแสดงค่าจริงของทั้งสองฝั่งเมื่อการทดสอบล้มเหลว - ไม่ต้องท่องจำ assertEqual กับสารพัดรูปแบบอีกสามสิบตัวของมันอีกต่อไป มีเครื่องมือสองอย่างที่ควรรู้จักตั้งแต่ตอนนี้ pytest.raises ใช้ตรวจว่ามี exception ถูกโยนออกมาจริง และ pytest.approx ใช้เปรียบเทียบเลขทศนิยมโดยไม่ต้องไปต่อสู้กับการปัดเศษแบบไบนารี
import pytest
def divide(a: float, b: float) -> float:
return a / b
def test_nominal() -> None:
assert divide(8, 4) == 2.0
def test_zero_division() -> None:
with pytest.raises(ZeroDivisionError):
divide(1, 0)
def test_floats() -> None:
assert 0.1 + 0.2 == pytest.approx(0.3)Fixture คือ dependency injection ของ pytest ประกาศพารามิเตอร์ที่มีชื่อตรงกับ fixture แล้ว pytest จะสร้างมันขึ้นมาและฉีดเข้าไปให้ ส่วน yield แยกขั้นตอนเตรียมการออกจากขั้นตอนเก็บกวาด - โค้ดที่อยู่หลังมันจะรัน แม้ว่าการทดสอบจะล้มเหลว พารามิเตอร์ scope (function โดยค่าเริ่มต้น, module, session) ควบคุมอายุของอ็อบเจกต์ จงวาง fixture ที่ใช้ร่วมกันไว้ใน conftest.py มันจะพร้อมใช้งานทั่วทั้งโฟลเดอร์โดยไม่ต้อง import สักบรรทัดเดียว
import sqlite3
import pytest
@pytest.fixture
def db():
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (name TEXT)")
yield conn
conn.close()
def test_insert(db) -> None:
db.execute("INSERT INTO users VALUES ('Ada')")
query = "SELECT name FROM users"
rows = db.execute(query).fetchall()
assert rows == [("Ada",)]@pytest.mark.parametrize เปลี่ยนการทดสอบหนึ่งตัวให้เป็นตารางของกรณีทดสอบ แต่ละทูเพิลกลายเป็นการทดสอบอิสระที่มีชื่อและกรองแยกได้ทีละตัว ซ้อน decorator parametrize หลายตัวเข้าด้วยกันเพื่อให้ได้ผลคูณคาร์ทีเซียนของพารามิเตอร์ จงตั้ง id ที่อ่านง่ายให้กรณีทดสอบของคุณผ่าน pytest.param(..., id='...') - รายงานความล้มเหลวควรเข้าใจได้โดยไม่ต้องเปิดไฟล์ต้นฉบับ
import pytest
def slugify(text: str) -> str:
return "-".join(text.lower().split())
@pytest.mark.parametrize(
("raw", "expected"),
[
("Hello World", "hello-world"),
(" Python rocks ", "python-rocks"),
pytest.param("", "", id="empty"),
],
)
def test_slugify(raw: str, expected: str) -> None:
assert slugify(raw) == expectedpytest -x --lf
pytest -k "slugify and not empty"
pytest -q --durations=5yield มีไว้ทำอะไร?pytest --lf ทำอะไร?