pytest Framework
pytest Framework
Section titled “pytest Framework”Introduction
Section titled “Introduction”pytest is the most popular Python testing framework. It’s simpler than unittest, with powerful features like fixtures, parametrization, and plugins.
Installation
Section titled “Installation”pip install pytest pytest-covBasic Tests
Section titled “Basic Tests”# test_calculator.py — no class needed!def test_add(): assert add(3, 5) == 8
def test_divide(): assert divide(10, 2) == 5.0
def test_divide_by_zero(): with pytest.raises(ValueError): divide(10, 0)Running Tests
Section titled “Running Tests”# Run all testspytest
# Verbose outputpytest -v
# Run specific filepytest test_calculator.py
# Run specific testpytest test_calculator.py::test_add
# Stop on first failurepytest -x
# Run tests matching patternpytest -k "user or login"Fixtures
Section titled “Fixtures”import pytest
@pytest.fixturedef sample_user(): """Create a sample user for testing.""" return {"name": "Alice", "age": 30}
@pytest.fixturedef db_connection(): """Setup and teardown database.""" conn = create_test_db() yield conn # Test runs here conn.close() # Cleanup
def test_user_name(sample_user): assert sample_user["name"] == "Alice"
def test_user_age(sample_user): assert sample_user["age"] == 30Parametrization
Section titled “Parametrization”@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
# Multiple parameters@pytest.mark.parametrize("username", ["user1", "user2", "admin"])@pytest.mark.parametrize("role", ["admin", "editor", "viewer"])def test_permissions(username, role): assert check_permission(username, role) is not Noneconftest.py
Section titled “conftest.py”# conftest.py — shared fixtures available to all testsimport pytest
@pytest.fixturedef app(): """Create test application.""" app = create_app() app.config["TESTING"] = True return app
@pytest.fixturedef client(app): """Test client for HTTP requests.""" return app.test_client()Markers
Section titled “Markers”import pytest
@pytest.mark.slowdef test_heavy_computation(): # This test takes a long time pass
@pytest.mark.skip(reason="Not implemented yet")def test_future_feature(): pass
@pytest.mark.xfail(reason="Known issue #123")def test_known_bug(): assert 1 / 0 # Expected to fail
# Run only slow tests# pytest -m slow
# Run all except slow# pytest -m "not slow"Mocking with pytest
Section titled “Mocking with pytest”from unittest.mock import Mock, patch
def test_get_user(monkeypatch): # Using monkeypatch def mock_get_user(user_id): return {"id": user_id, "name": "Alice"}
monkeypatch.setattr("mymodule.get_user", mock_get_user) result = get_user(1) assert result["name"] == "Alice"
# Using mocker plugin (pytest-mock)def test_get_user(mocker): mock_response = mocker.patch("requests.get") mock_response.return_value.json.return_value = {"name": "Alice"}
result = fetch_user(1) assert result["name"] == "Alice"Coverage
Section titled “Coverage”# Run with coveragepytest --cov=src --cov-report=html
# Coverage configuration (setup.cfg)# [tool:pytest]# testpaths = tests# python_files = test_*.pyBest Practices
Section titled “Best Practices”- Use plain
assert— no need for special assertion methods - Use fixtures for reusable test setup and cleanup
- Use parametrization to test multiple cases without repetition
- Use conftest.py for shared fixtures across test files
- Keep tests fast — no network calls in unit tests
- Name test files
test_*.pyand functionstest_*
Practice Exercises
Section titled “Practice Exercises”Exercise 1: Convert your unittest Calculator tests to pytest style with parametrization.
Exercise 2: Create a fixture that creates temporary files for testing file operations.