Kiểm thử tự động giúp bạn tự tin sửa và mở rộng chương trình. Bài này dạy pytest, công cụ kiểm thử phổ biến nhất của Python: viết test, fixture, tham số hóa và đo độ phủ.
- Viết test với assert trong pytest
- Kiểm tra ngoại lệ bằng pytest.raises
- Dùng fixture và parametrize
- Đo độ phủ mã (coverage)
- Test case
- Một tình huống kiểm tra cụ thể.
- Fixture
- Dữ liệu/tài nguyên chuẩn bị sẵn cho test.
- Parametrize
- Chạy một test với nhiều bộ dữ liệu.
- Coverage
- Tỉ lệ dòng mã được test chạy qua.
Khái niệm và lý thuyết
pytest tự tìm các tệp test_*.py và hàm test_*, rồi chạy. Chỉ cần câu lệnh assert thông thường; khi sai, pytest hiển thị chi tiết giá trị hai vế. Một test tốt: nhanh, độc lập, lặp lại được, kiểm tra một ý.
Nên kiểm thử các trường hợp biên: danh sách rỗng, số 0, số âm, giá trị đúng ngưỡng (điểm 5.0), dữ liệu sai định dạng.
Test đầu tiên
uv add --dev pytest pytest-cov# src/diem.py
def xep_loai(d: float) -> str:
if not 0 <= d <= 10:
raise ValueError("Điểm không hợp lệ")
if d >= 8.5: return "Giỏi"
if d >= 7: return "Khá"
if d >= 5: return "Trung bình"
return "Yếu"
# tests/test_diem.py
import pytest
from diem import xep_loai
def test_gioi():
assert xep_loai(9) == "Giỏi"
@pytest.mark.parametrize("d, kq", [(8.5, "Giỏi"), (7, "Khá"), (5, "Trung bình"), (4.9, "Yếu")])
def test_nguong(d, kq):
assert xep_loai(d) == kq
def test_diem_sai():
with pytest.raises(ValueError, match="không hợp lệ"):
xep_loai(11)uv run pytest -v
uv run pytest --cov=srcFixture
@pytest.fixture
def lop_mau():
return [{"ten": "An", "diem": 8}, {"ten": "Bình", "diem": 4}]
def test_so_dat(lop_mau):
assert sum(sv["diem"] >= 5 for sv in lop_mau) == 1
def test_ghi_tep(tmp_path): # fixture có sẵn: thư mục tạm
tep = tmp_path / "a.txt"
tep.write_text("xin chào", encoding="utf-8")
assert tep.read_text(encoding="utf-8") == "xin chào"Giả lập (mock)
def test_lay_nhiet_do(monkeypatch):
class GiaPhanHoi:
def raise_for_status(self): pass
def json(self): return {"current": {"temperature_2m": 25}}
monkeypatch.setattr("requests.get", lambda *a, **k: GiaPhanHoi())
from thoi_tiet import nhiet_do
assert nhiet_do() == 25Mock giúp test không phụ thuộc mạng, chạy nhanh và ổn định.
Quy trình TDD: viết test trước (đỏ) → viết mã vừa đủ cho test qua (xanh) → tối ưu mã (refactor). Rất hữu ích khi làm bài tập thuật toán.
Test gọi API thật hoặc phụ thuộc giờ hệ thống sẽ lúc qua lúc trượt. Dùng monkeypatch hoặc truyền thời gian vào hàm như tham số.
- Viết test cho hàm kiểm tra số nguyên tố (gồm 0, 1, 2, số âm).
- Viết test tham số hóa cho hàm chuyển °C sang °F.
- Dùng tmp_path kiểm thử hàm ghi CSV ở Bài 15.
- Đạt độ phủ trên 80% cho dự án Bài 10.
- pytest: tệp test_*.py, hàm test_*, dùng assert.
- parametrize kiểm thử nhiều bộ dữ liệu.
- fixture chuẩn bị dữ liệu; tmp_path, monkeypatch có sẵn.
- Đo độ phủ bằng pytest-cov.