pytest-python
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePytest Testing for Python
面向Python的Pytest测试
Quick Reference
快速参考
Test Discovery
测试发现
Pytest auto-discovers tests matching:
- Files: or
test_*.py*_test.py - Functions: prefix
test_* - Classes: prefix (no
Test*)__init__
Pytest会自动发现符合以下规则的测试:
- 文件:或
test_*.py*_test.py - 函数:带前缀
test_ - 类:带前缀(无
Test*方法)__init__
Running Tests
运行测试
bash
pytest # Run all tests
pytest test_mod.py # Single module
pytest tests/ # Directory
pytest -k "name" # By keyword
pytest -m slow # By marker
pytest test_mod.py::test_func # Specific test
pytest --durations=10 # Show slowest testsbash
pytest # Run all tests
pytest test_mod.py # Single module
pytest tests/ # Directory
pytest -k "name" # By keyword
pytest -m slow # By marker
pytest test_mod.py::test_func # Specific test
pytest --durations=10 # Show slowest testsFixtures
Fixtures
Fixtures provide reusable test dependencies.
Fixtures用于提供可复用的测试依赖。
Basic Fixture
基础Fixture
python
import pytest
@pytest.fixture
def sample_data():
return {"key": "value"}
def test_example(sample_data):
assert sample_data["key"] == "value"python
import pytest
@pytest.fixture
def sample_data():
return {"key": "value"}
def test_example(sample_data):
assert sample_data["key"] == "value"Fixture Scopes
Fixture作用域
python
@pytest.fixture(scope="function") # Default: per test
@pytest.fixture(scope="class") # Per test class
@pytest.fixture(scope="module") # Per module
@pytest.fixture(scope="session") # Entire sessionpython
@pytest.fixture(scope="function") # Default: per test
@pytest.fixture(scope="class") # Per test class
@pytest.fixture(scope="module") # Per module
@pytest.fixture(scope="session") # Entire sessionFixture with Teardown (yield)
带销毁逻辑的Fixture(yield)
python
@pytest.fixture
def db_connection():
conn = create_connection()
yield conn
conn.close() # Cleanup after testpython
@pytest.fixture
def db_connection():
conn = create_connection()
yield conn
conn.close() # Cleanup after testFactory Fixture
工厂Fixture
python
@pytest.fixture
def make_user():
def _make_user(name, role="user"):
return {"name": name, "role": role}
return _make_user
def test_users(make_user):
admin = make_user("Alice", role="admin")
user = make_user("Bob")python
@pytest.fixture
def make_user():
def _make_user(name, role="user"):
return {"name": name, "role": role}
return _make_user
def test_users(make_user):
admin = make_user("Alice", role="admin")
user = make_user("Bob")Parametrized Fixture
参数化Fixture
python
@pytest.fixture(params=["mysql", "postgres", "sqlite"])
def database(request):
return create_db(request.param)python
@pytest.fixture(params=["mysql", "postgres", "sqlite"])
def database(request):
return create_db(request.param)Parametrization
参数化
Run tests with multiple inputs.
python
@pytest.mark.parametrize("input,expected", [
(1, 2),
(2, 4),
(3, 6),
])
def test_double(input, expected):
assert input * 2 == expected使用多组输入运行测试。
python
@pytest.mark.parametrize("input,expected", [
(1, 2),
(2, 4),
(3, 6),
])
def test_double(input, expected):
assert input * 2 == expectedMultiple Parameters (Combinations)
多参数(组合测试)
python
@pytest.mark.parametrize("x", [1, 2])
@pytest.mark.parametrize("y", [10, 20])
def test_multiply(x, y): # Runs 4 combinations
assert x * y > 0python
@pytest.mark.parametrize("x", [1, 2])
@pytest.mark.parametrize("y", [10, 20])
def test_multiply(x, y): # Runs 4 combinations
assert x * y > 0With Expected Failures
预期失败场景
python
@pytest.mark.parametrize("input,expected", [
(1, 1),
pytest.param(0, 1, marks=pytest.mark.xfail),
])
def test_factorial(input, expected):
assert factorial(input) == expectedpython
@pytest.mark.parametrize("input,expected", [
(1, 1),
pytest.param(0, 1, marks=pytest.mark.xfail),
])
def test_factorial(input, expected):
assert factorial(input) == expectedMarkers
标记
Built-in Markers
内置标记
python
@pytest.mark.skip(reason="Not implemented")
def test_feature(): ...
@pytest.mark.skipif(sys.platform == "win32", reason="Unix only")
def test_unix(): ...
@pytest.mark.xfail(reason="Known bug")
def test_buggy(): ...python
@pytest.mark.skip(reason="Not implemented")
def test_feature(): ...
@pytest.mark.skipif(sys.platform == "win32", reason="Unix only")
def test_unix(): ...
@pytest.mark.xfail(reason="Known bug")
def test_buggy(): ...Custom Markers
自定义标记
Register in or :
pytest.inipyproject.tomlini
[pytest]
markers =
slow: marks tests as slow
integration: integration testspython
@pytest.mark.slow
def test_slow_operation(): ...Run: or
pytest -m slowpytest -m "not slow"在或中注册:
pytest.inipyproject.tomlini
[pytest]
markers =
slow: marks tests as slow
integration: integration testspython
@pytest.mark.slow
def test_slow_operation(): ...运行命令: 或
pytest -m slowpytest -m "not slow"Assertions
断言
Basic Assertions
基础断言
python
assert value == expected
assert value != other
assert value is None
assert value is not None
assert value in collection
assert isinstance(obj, MyClass)python
assert value == expected
assert value != other
assert value is None
assert value is not None
assert value in collection
assert isinstance(obj, MyClass)Floating Point
浮点数断言
python
assert 0.1 + 0.2 == pytest.approx(0.3)
assert result == pytest.approx(expected, rel=1e-3)python
assert 0.1 + 0.2 == pytest.approx(0.3)
assert result == pytest.approx(expected, rel=1e-3)Exception Testing
异常测试
python
def test_raises():
with pytest.raises(ValueError):
int("invalid")
def test_raises_with_match():
with pytest.raises(ValueError, match=r"invalid.*"):
raise ValueError("invalid input")
def test_raises_inspect():
with pytest.raises(ValueError) as exc_info:
raise ValueError("test error")
assert "test" in str(exc_info.value)python
def test_raises():
with pytest.raises(ValueError):
int("invalid")
def test_raises_with_match():
with pytest.raises(ValueError, match=r"invalid.*"):
raise ValueError("invalid input")
def test_raises_inspect():
with pytest.raises(ValueError) as exc_info:
raise ValueError("test error")
assert "test" in str(exc_info.value)Monkeypatch (Mocking)
Monkeypatch(Mocking)
Patching Functions
打补丁替换函数
python
def test_api_call(monkeypatch):
def mock_get(*args, **kwargs):
return {"status": "ok"}
monkeypatch.setattr("mymodule.api.get", mock_get)
result = mymodule.fetch_data()
assert result["status"] == "ok"python
def test_api_call(monkeypatch):
def mock_get(*args, **kwargs):
return {"status": "ok"}
monkeypatch.setattr("mymodule.api.get", mock_get)
result = mymodule.fetch_data()
assert result["status"] == "ok"Environment Variables
环境变量操作
python
def test_with_env(monkeypatch):
monkeypatch.setenv("API_KEY", "test-key")
assert os.environ["API_KEY"] == "test-key"
def test_without_env(monkeypatch):
monkeypatch.delenv("API_KEY", raising=False)python
def test_with_env(monkeypatch):
monkeypatch.setenv("API_KEY", "test-key")
assert os.environ["API_KEY"] == "test-key"
def test_without_env(monkeypatch):
monkeypatch.delenv("API_KEY", raising=False)Dictionary Values
字典值修改
python
def test_config(monkeypatch):
monkeypatch.setitem(app.config, "DEBUG", True)python
def test_config(monkeypatch):
monkeypatch.setitem(app.config, "DEBUG", True)Built-in Fixtures
内置Fixtures
| Fixture | Purpose |
|---|---|
| Temporary directory (pathlib.Path) |
| Session-scoped temp directories |
| Capture stdout/stderr |
| Capture log messages |
| Dynamic patching |
| Fixture/test metadata |
| Fixture | 用途 |
|---|---|
| 临时目录(pathlib.Path对象) |
| 会话级别的临时目录 |
| 捕获stdout/stderr输出 |
| 捕获日志消息 |
| 动态打补丁 |
| Fixture/测试元数据 |
Examples
示例
python
def test_output(capsys):
print("hello")
captured = capsys.readouterr()
assert captured.out == "hello\n"
def test_logging(caplog):
import logging
logging.warning("test warning")
assert "test warning" in caplog.text
def test_temp_file(tmp_path):
file = tmp_path / "test.txt"
file.write_text("content")
assert file.read_text() == "content"python
def test_output(capsys):
print("hello")
captured = capsys.readouterr()
assert captured.out == "hello\n"
def test_logging(caplog):
import logging
logging.warning("test warning")
assert "test warning" in caplog.text
def test_temp_file(tmp_path):
file = tmp_path / "test.txt"
file.write_text("content")
assert file.read_text() == "content"Project Structure
项目结构
Recommended layout:
project/
├── pyproject.toml
├── src/
│ └── mypackage/
│ ├── __init__.py
│ └── module.py
└── tests/
├── conftest.py # Shared fixtures
├── test_module.py
└── unit/
└── test_specific.py推荐目录结构:
project/
├── pyproject.toml
├── src/
│ └── mypackage/
│ ├── __init__.py
│ └── module.py
└── tests/
├── conftest.py # 共享fixtures
├── test_module.py
└── unit/
└── test_specific.pyconftest.py
conftest.py
Shared fixtures available to all tests in directory:
python
undefined目录下所有测试都可以使用的共享fixture:
python
undefinedtests/conftest.py
tests/conftest.py
import pytest
@pytest.fixture
def app():
return create_app(testing=True)
@pytest.fixture
def client(app):
return app.test_client()
undefinedimport pytest
@pytest.fixture
def app():
return create_app(testing=True)
@pytest.fixture
def client(app):
return app.test_client()
undefinedConfiguration
配置
pyproject.toml
pyproject.toml配置示例
toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "-v --strict-markers"
markers = [
"slow: marks tests as slow",
"integration: integration tests",
]
filterwarnings = [
"ignore::DeprecationWarning",
]toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "-v --strict-markers"
markers = [
"slow: marks tests as slow",
"integration: integration tests",
]
filterwarnings = [
"ignore::DeprecationWarning",
]Best Practices
最佳实践
- One assertion focus per test - Test one behavior per function
- Descriptive names -
test_user_creation_with_invalid_email_raises_error - Use fixtures - Avoid setup duplication
- Isolate tests - No shared state between tests
- Fast unit tests - Mark slow tests with
@pytest.mark.slow - Parametrize - Use parametrize over copy-paste tests
- Test edge cases - Empty inputs, boundaries, errors
- 单测试单断言聚焦 - 每个测试函数仅测试一种行为
- 描述性命名 - 例如
test_user_creation_with_invalid_email_raises_error - 使用fixtures - 避免重复的配置代码
- 测试隔离 - 测试之间不要共享状态
- 快速单元测试 - 给慢速测试添加标记
@pytest.mark.slow - 参数化 - 使用参数化代替复制粘贴测试用例
- 测试边界场景 - 空输入、边界值、错误场景
References
参考资料
- Fixtures Guide - Advanced fixture patterns
- Patterns Guide - Common testing patterns
- Fixtures指南 - 进阶fixture模式
- 模式指南 - 常见测试模式