返回 Skill 列表
extension
分类: 开发与工程无需 API Key

pytest

使用pytest框架进行Python测试的专业指导。在以下情况下使用:(1) 编写单元测试或集成测试,(2) 设置用于安装/拆卸的测试夹具,(3) 用多个输入参数化测试,(4) 使用标记(如skip、xfail、自定义),(5) 使用monkeypatch或pytest-mock进行模拟/打补丁,(6) 配置pytest(如pytest.ini、pyproject.toml、conftest.py),(7) 运行带有覆盖率的测试,(8) 调试测试失败,或者(9) 测试异步代码、FastAPI、Django或Flask应用程序。

person作者: jakexiaohubgithub

Pytest Testing Guide

Write Python tests efficiently with pytest.

Quick Start

pip install pytest
# test_example.py
def test_addition():
    assert 1 + 1 == 2

Run: pytest or pytest -v

Core Concepts

| Concept | Example | |---------|---------| | Test function | def test_something(): | | Assertion | assert result == expected | | Fixture | @pytest.fixture - dependency injection | | Marker | @pytest.mark.skip - test metadata | | Parametrize | @pytest.mark.parametrize - multiple inputs |

Essential Patterns

Basic Test

def test_function():
    result = my_function(1, 2)
    assert result == 3

Exception Testing

import pytest

def test_raises():
    with pytest.raises(ValueError, match="invalid"):
        raise ValueError("invalid input")

Fixture (Setup/Teardown)

@pytest.fixture
def database():
    db = create_db()
    yield db          # provide to test
    db.cleanup()      # teardown

def test_query(database):
    assert database.query("SELECT 1")

Parametrize

@pytest.mark.parametrize("input,expected", [
    (1, 2),
    (2, 4),
    (3, 6),
])
def test_double(input, expected):
    assert input * 2 == expected

Mocking

def test_mock(monkeypatch):
    monkeypatch.setattr("module.func", lambda: "mocked")
    monkeypatch.setenv("API_KEY", "test-key")

Reference Files

| Topic | File | When to Use | |-------|------|-------------| | Basics | basics.md | Test discovery, assertions, running tests, config | | Fixtures | fixtures.md | Setup/teardown, scopes, factories, conftest.py | | Markers | markers.md | skip, xfail, parametrize, custom markers | | Mocking | mocking.md | monkeypatch, pytest-mock, spies, async mocks | | Plugins | plugins.md | Coverage, parallel, async, Django, Flask, FastAPI |

Common Commands

pytest                    # Run all tests
pytest -v                 # Verbose
pytest -s                 # Show prints
pytest -x                 # Stop on first failure
pytest -k "pattern"       # Run matching tests
pytest -m slow            # Run by marker
pytest --cov=mypackage    # With coverage
pytest -n auto            # Parallel (xdist)
pytest --lf               # Run last failed
pytest --pdb              # Debug on failure

Project Structure

project/
├── src/
│   └── mypackage/
│       └── module.py
├── tests/
│   ├── conftest.py      # Shared fixtures
│   ├── test_unit.py
│   └── test_integration.py
├── pytest.ini           # Or pyproject.toml
└── requirements-test.txt

Configuration (pyproject.toml)

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --tb=short"
markers = [
    "slow: slow tests",
    "integration: integration tests",
]

Built-in Fixtures

| Fixture | Purpose | |---------|---------| | tmp_path | Temp directory (pathlib.Path) | | tmpdir | Temp directory (legacy) | | capsys | Capture stdout/stderr | | caplog | Capture logging | | monkeypatch | Patch attributes/env vars | | request | Test/fixture metadata |

Fixture Scopes

@pytest.fixture(scope="function")  # Default - per test
@pytest.fixture(scope="class")     # Per test class
@pytest.fixture(scope="module")    # Per file
@pytest.fixture(scope="session")   # Per test run

Best Practices

  • Name tests descriptively: test_user_login_with_invalid_password_fails
  • One assertion concept per test
  • Use fixtures for reusable setup
  • Use conftest.py for shared fixtures
  • Use markers to categorize tests (unit, integration, slow)
  • Run with coverage: pytest --cov --cov-report=html
  • Use parametrize to reduce duplicate tests