← Back to Articles
Tutorial

Python Pytest – Complete Testing Framework Guide

Master pytest testing framework with simple syntax, assertions, fixtures, parametrization, and best practices for writing robust Python tests.

📌 Python pytest, testing framework, pytest tutorial, test automation, pytest fixtures, pytest assertions

pytest is a popular Python testing framework emphasizing simplicity and accessibility. Unlike verbose xUnit frameworks, pytest uses standard Python functions with plain assert statements—making tests natural and readable.

pytest offers five key advantages: simple syntax using standard assert statements, automatic test discovery (finds test_*.py files), informative failure output with detailed debugging info, rich plugin ecosystem, and low entry barrier for beginners.

Installation is straightforward: pip install pytest. Tests are discovered automatically in files named test_*.py or *_test.py, and in functions/test methods prefixed with test_. No explicit registration required—pytest finds everything automatically.

Running tests offers flexibility: pytest alone runs all tests, pytest -v provides verbose output, and pytest test_example.py -v targets a specific file. Success appears as dots (.) or 'PASSED', failures show detailed tracebacks with assertion comparisons.

Test organization can use classes starting with 'Test' (no inheritance required) for grouping related tests. pytest automatically discovers these test methods without any special base classes or setup methods.

Code Examples

Basic Test Example

# test_example.py

def add(x, y):
    return x + y

def test_add_positive_numbers():
    assert add(1, 2) == 3

def test_add_negative_numbers():
    assert add(-1, -2) == -3

def test_add_zero():
    assert add(5, 0) == 5

# Run with: pytest test_example.py -v

Testing with Expected Exceptions

def divide(x, y):
    if y == 0:
        raise ValueError("Cannot divide by zero")
    return x / y

def test_divide_by_zero():
    with pytest.raises(ValueError) as exc_info:
        divide(10, 0)
    assert str(exc_info.value) == "Cannot divide by zero"

def test_divide_normal():
    assert divide(10, 2) == 5

Organizing Tests in Classes

class Calculator:
    def add(self, x, y):
        return x + y

    def multiply(self, x, y):
        return x * y

class TestCalculator:
    def test_add(self):
        calc = Calculator()
        assert calc.add(2, 3) == 5
        assert calc.add(-1, 1) == 0

    def test_multiply(self):
        calc = Calculator()
        assert calc.multiply(3, 4) == 12
        assert calc.multiply(-2, 5) == -10

# No inheritance needed - just use 'Test' prefix

Parametrized Tests

import pytest

@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

# Runs the same test 4 times with different inputs

Test Fixtures

import pytest

@pytest.fixture
def sample_data():
    """Setup fixture that provides test data"""
    return {"name": "Test", "value": 42}

@pytest.fixture
def database():
    """Setup/teardown fixture for database"""
    db = {"data": []}
    yield db  # This is provided to the test
    # Teardown: cleanup code here
    db.clear()

def test_with_fixture(sample_data):
    assert sample_data["name"] == "Test"
    assert sample_data["value"] == 42

def test_database_operations(database):
    database["data"].append("item1")
    assert len(database["data"]) == 1

Setup and Teardown with Class Fixtures

import pytest

class TestDatabase:
    @pytest.fixture(autouse=True)
    def setup_teardown(self):
        # Setup: runs before each test
        self.db = []
        yield
        # Teardown: runs after each test
        self.db.clear()

    def test_insert(self):
        self.db.append("item1")
        assert len(self.db) == 1

    def test_delete(self):
        self.db.append("item1")
        self.db.remove("item1")
        assert len(self.db) == 0

Running Specific Tests

# Run all tests in directory
pytest

# Run with verbose output
pytest -v

# Run specific file
pytest test_calculator.py -v

# Run specific test function
pytest test_calculator.py::test_add -v

# Run tests matching pattern
pytest -k "add" -v  # Runs tests containing "add"

# Stop on first failure
pytest -x

# Show print statements
pytest -s

More Python Tutorials