Some checks failed
K8S Fission Deployment / Deployment fission functions (push) Failing after 12s
188 lines
4.1 KiB
Markdown
188 lines
4.1 KiB
Markdown
---
|
|
name: pytest
|
|
description: |
|
|
Python testing with pytest framework for unit, integration, and API tests.
|
|
Use when: (1) Writing test cases for Python code, (2) Setting up pytest fixtures,
|
|
(3) Testing async functions with pytest-asyncio, (4) Mocking dependencies,
|
|
(5) Parameterizing tests, (6) Testing FastAPI/Flask endpoints, (7) Setting up test coverage,
|
|
(8) Creating test factories with factory_boy, (9) Configuring CI/CD test pipelines.
|
|
---
|
|
|
|
# Pytest Testing Skill
|
|
|
|
Comprehensive testing patterns for Python applications using pytest.
|
|
|
|
## Quick Reference
|
|
|
|
| Feature | Reference File |
|
|
|---------|----------------|
|
|
| Fixtures, conftest, scopes | [references/fixtures.md](references/fixtures.md) |
|
|
| Async testing, pytest-asyncio | [references/async-testing.md](references/async-testing.md) |
|
|
| Mocking, patching, spies | [references/mocking.md](references/mocking.md) |
|
|
| FastAPI/Flask endpoint testing | [references/api-testing.md](references/api-testing.md) |
|
|
|
|
## Dependencies
|
|
|
|
```toml
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"pytest>=8.0.0",
|
|
"pytest-asyncio>=0.24.0",
|
|
"pytest-cov>=4.1.0",
|
|
"httpx>=0.28.0", # Async HTTP client for API tests
|
|
"factory-boy>=3.3.0", # Test factories
|
|
"faker>=33.0.0", # Fake data generation
|
|
]
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### pyproject.toml
|
|
|
|
```toml
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
testpaths = ["tests"]
|
|
python_files = ["test_*.py"]
|
|
python_functions = ["test_*"]
|
|
addopts = "-v --tb=short"
|
|
filterwarnings = ["ignore::DeprecationWarning"]
|
|
|
|
[tool.coverage.run]
|
|
source = ["app"]
|
|
omit = ["*/tests/*", "*/__init__.py"]
|
|
```
|
|
|
|
## Basic Test Structure
|
|
|
|
```python
|
|
import pytest
|
|
|
|
# Simple test function
|
|
def test_addition():
|
|
assert 1 + 1 == 2
|
|
|
|
# Test class (group related tests)
|
|
class TestCalculator:
|
|
def test_add(self):
|
|
assert add(2, 3) == 5
|
|
|
|
def test_subtract(self):
|
|
assert subtract(5, 3) == 2
|
|
|
|
# Expected exceptions
|
|
def test_division_by_zero():
|
|
with pytest.raises(ZeroDivisionError):
|
|
divide(1, 0)
|
|
|
|
# Parametrized tests
|
|
@pytest.mark.parametrize("input,expected", [
|
|
(1, 1),
|
|
(2, 4),
|
|
(3, 9),
|
|
])
|
|
def test_square(input, expected):
|
|
assert square(input) == expected
|
|
```
|
|
|
|
## Fixtures
|
|
|
|
```python
|
|
import pytest
|
|
|
|
@pytest.fixture
|
|
def sample_user():
|
|
return {"name": "John", "email": "john@example.com"}
|
|
|
|
@pytest.fixture
|
|
def db_connection():
|
|
conn = create_connection()
|
|
yield conn # Test runs here
|
|
conn.close() # Cleanup after test
|
|
|
|
# Use fixture in test
|
|
def test_user_name(sample_user):
|
|
assert sample_user["name"] == "John"
|
|
```
|
|
|
|
## Async Testing
|
|
|
|
```python
|
|
import pytest
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_function():
|
|
result = await async_operation()
|
|
assert result == "success"
|
|
|
|
@pytest.fixture
|
|
async def async_client():
|
|
async with AsyncClient() as client:
|
|
yield client
|
|
```
|
|
|
|
## Mocking
|
|
|
|
```python
|
|
from unittest.mock import patch, MagicMock, AsyncMock
|
|
|
|
def test_with_mock():
|
|
with patch("module.external_api") as mock_api:
|
|
mock_api.return_value = {"data": "mocked"}
|
|
result = function_using_api()
|
|
assert result["data"] == "mocked"
|
|
mock_api.assert_called_once()
|
|
|
|
# Async mock
|
|
@pytest.mark.asyncio
|
|
async def test_async_mock():
|
|
with patch("module.async_call", new_callable=AsyncMock) as mock:
|
|
mock.return_value = "result"
|
|
result = await function_with_async_call()
|
|
assert result == "result"
|
|
```
|
|
|
|
## Running Tests
|
|
|
|
```bash
|
|
# Run all tests
|
|
pytest
|
|
|
|
# Verbose output
|
|
pytest -v
|
|
|
|
# Run specific file
|
|
pytest tests/test_users.py
|
|
|
|
# Run specific test
|
|
pytest tests/test_users.py::test_create_user
|
|
|
|
# Run with coverage
|
|
pytest --cov=app --cov-report=term-missing
|
|
|
|
# Stop on first failure
|
|
pytest -x
|
|
|
|
# Run last failed tests
|
|
pytest --lf
|
|
|
|
# Run tests matching pattern
|
|
pytest -k "user and not delete"
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
project/
|
|
├── app/
|
|
│ ├── __init__.py
|
|
│ ├── main.py
|
|
│ └── services/
|
|
├── tests/
|
|
│ ├── __init__.py
|
|
│ ├── conftest.py # Shared fixtures
|
|
│ ├── test_main.py
|
|
│ └── test_services/
|
|
└── pyproject.toml
|
|
```
|