pytest

📁 1mangesh1/dev-skills-collection 📅 12 days ago
4
总安装量
4
周安装量
#48390
全站排名
安装命令
npx skills add https://github.com/1mangesh1/dev-skills-collection --skill pytest

Agent 安装分布

opencode 4
gemini-cli 4
claude-code 4
cursor 4
kilo 3
github-copilot 3

Skill 文档

pytest

Complete Python testing toolkit with pytest.

Running Tests

# Run all tests
pytest

# Run specific file/directory
pytest tests/test_api.py
pytest tests/

# Run specific test
pytest tests/test_api.py::test_login
pytest tests/test_api.py::TestUserClass::test_create

# Verbose output
pytest -v
pytest -vv  # Extra verbose

# Stop on first failure
pytest -x

# Run last failed
pytest --lf

# Run failed first, then rest
pytest --ff

# Show print output
pytest -s

# Parallel (requires pytest-xdist)
pytest -n auto
pytest -n 4

Fixtures

import pytest

# Basic fixture
@pytest.fixture
def user():
    return {"name": "Alice", "email": "alice@test.com"}

# Fixture with teardown
@pytest.fixture
def db_connection():
    conn = create_connection()
    yield conn
    conn.close()

# Autouse fixture (runs for every test)
@pytest.fixture(autouse=True)
def reset_state():
    State.reset()
    yield
    State.cleanup()

# Scoped fixtures
@pytest.fixture(scope="module")
def expensive_resource():
    return load_heavy_thing()

# scope options: "function" (default), "class", "module", "package", "session"

# Fixture with params
@pytest.fixture(params=["sqlite", "postgres", "mysql"])
def db_engine(request):
    return create_engine(request.param)

# Using fixtures
def test_user_name(user):
    assert user["name"] == "Alice"

Parametrize

# Basic parametrize
@pytest.mark.parametrize("input,expected", [
    ("hello", 5),
    ("", 0),
    ("world", 5),
])
def test_string_length(input, expected):
    assert len(input) == expected

# Multiple parameters
@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_combinations(x, y):
    pass  # Runs 4 times: (0,2), (0,3), (1,2), (1,3)

# With IDs
@pytest.mark.parametrize("input,expected", [
    pytest.param("admin", True, id="admin-user"),
    pytest.param("guest", False, id="guest-user"),
])
def test_is_admin(input, expected):
    assert is_admin(input) == expected

Mocking

from unittest.mock import Mock, patch, MagicMock

# patch decorator
@patch("myapp.services.send_email")
def test_registration(mock_send):
    register_user("alice@test.com")
    mock_send.assert_called_once_with("alice@test.com", subject="Welcome")

# patch as context manager
def test_api_call():
    with patch("myapp.client.requests.get") as mock_get:
        mock_get.return_value.json.return_value = {"status": "ok"}
        result = fetch_status()
        assert result == "ok"

# Mock fixture (pytest-mock)
def test_with_mocker(mocker):
    mock_db = mocker.patch("myapp.db.query")
    mock_db.return_value = [{"id": 1}]
    result = get_users()
    assert len(result) == 1

# Side effects
mock_func = Mock(side_effect=ValueError("boom"))
mock_func = Mock(side_effect=[1, 2, 3])  # Returns sequentially
mock_func = Mock(side_effect=lambda x: x * 2)

# Spec mocking (catches attribute errors)
mock_obj = Mock(spec=MyClass)

Markers

# Skip
@pytest.mark.skip(reason="Not implemented yet")
def test_future_feature():
    pass

# Skip conditionally
@pytest.mark.skipif(sys.platform == "win32", reason="Unix only")
def test_unix_permissions():
    pass

# Expected failure
@pytest.mark.xfail(reason="Known bug #123")
def test_known_bug():
    pass

# Custom markers
@pytest.mark.slow
def test_heavy_computation():
    pass

# Run by marker: pytest -m slow
# Run excluding: pytest -m "not slow"

# Register markers in pytest.ini or pyproject.toml
# [tool.pytest.ini_options]
# markers = ["slow: marks tests as slow"]

Assertions

# Basic
assert result == expected
assert item in collection
assert value is None

# Exception testing
with pytest.raises(ValueError):
    int("not_a_number")

with pytest.raises(ValueError, match="invalid literal"):
    int("not_a_number")

# Approximate
assert 0.1 + 0.2 == pytest.approx(0.3)
assert result == pytest.approx(expected, rel=1e-3)

# Warnings
with pytest.warns(DeprecationWarning):
    deprecated_function()

Coverage

# Install
pip install pytest-cov

# Run with coverage
pytest --cov=myapp
pytest --cov=myapp --cov-report=html
pytest --cov=myapp --cov-report=term-missing
pytest --cov=myapp --cov-branch  # Branch coverage

# Minimum threshold
pytest --cov=myapp --cov-fail-under=80

conftest.py

# tests/conftest.py - Shared fixtures available to all tests

import pytest

@pytest.fixture
def app():
    """Create test application."""
    app = create_app(testing=True)
    yield app

@pytest.fixture
def client(app):
    """Create test client."""
    return app.test_client()

@pytest.fixture
def auth_headers():
    """Create auth headers."""
    token = create_test_token()
    return {"Authorization": f"Bearer {token}"}

pyproject.toml Config

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "-v --tb=short --strict-markers"
markers = [
    "slow: marks tests as slow",
    "integration: integration tests",
]
filterwarnings = [
    "ignore::DeprecationWarning",
]

Reference

For advanced patterns, plugins, and async testing: references/patterns.md