写一个测试
新建文件 test_xxx.py,用 test_ 开头的函数:
# test_calc.py
def add(a, b):
return a + b
def test_add():
assert add(1, 2) == 3
def test_add_negative():
assert add(-1, -1) == -2
运行:
pip install pytest
pytest # 自动发现并跑所有 test_*.py
用 assert,不用 self.assertEqual
pytest 比 unittest 简洁多了:
# unittest(老)
self.assertEqual(actual, expected)
self.assertTrue(x)
self.assertIn(item, container)
# pytest(新)
assert actual == expected
assert x
assert item in container
pytest 报错时会自动展示对比信息——不需要手写 message。
fixture:测试用例的"准备工作"
import pytest
@pytest.fixture
def sample_user():
return {"name": "WadeLy", "age": 30}
def test_name(sample_user):
assert sample_user["name"] == "WadeLy"
def test_age(sample_user):
assert sample_user["age"] == 30
把 fixture 名当函数参数,pytest 自动注入。
fixture 的清理
@pytest.fixture
def db():
conn = connect()
yield conn # 测试用例拿到的是 conn
conn.close() # 测试结束后自动清理
跟上下文管理器异曲同工。
conftest.py:跨文件共享 fixture
放一个 conftest.py 在测试目录里,里面的 fixture 自动被所有测试看见:
# conftest.py
import pytest
@pytest.fixture
def app():
return create_test_app()
# 任意 test_*.py 都能直接用 app
def test_index(app):
...
parametrize:一个测试跑多组数据
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
])
def test_add(a, b, expected):
assert add(a, b) == expected
跑出来是 4 个测试用例,分别打印通过/失败。
测试异常
def divide(a, b):
if b == 0:
raise ValueError("b 不能为 0")
return a / b
def test_divide_zero():
with pytest.raises(ValueError, match="不能为 0"):
divide(10, 0)
mock:替换外部依赖
from unittest.mock import patch
def fetch_user(uid):
return requests.get(f"https://api/{uid}").json()
def test_fetch_user():
with patch("requests.get") as mock_get:
mock_get.return_value.json.return_value = {"name": "WadeLy"}
result = fetch_user(1)
assert result["name"] == "WadeLy"
不真发 HTTP——把 requests.get 换成假货。
或者用 fixture 形式:
def test_fetch_user(monkeypatch):
monkeypatch.setattr("requests.get", lambda url: FakeResponse(...))
...
标记 skip / xfail
@pytest.mark.skip(reason="还没实现")
def test_future_feature(): ...
@pytest.mark.skipif(sys.platform == "win32", reason="只在 Linux 跑")
def test_unix_only(): ...
@pytest.mark.xfail
def test_known_bug(): ...
覆盖率
pip install pytest-cov
pytest --cov=mypackage --cov-report=html
生成 HTML 报告,告诉你哪些行没被测到。
项目结构推荐
myproject/
├── src/
│ └── mypkg/
│ ├── __init__.py
│ └── calc.py
└── tests/
├── conftest.py
├── test_calc.py
└── test_api.py
pytest tests/ 跑全部。
下一篇讲调试技巧。