pytest

Keywords: pytest,testing,python

pytest is the most popular Python testing framework, offering simple syntax, powerful fixtures, and extensive plugin ecosystem that makes writing and running tests enjoyable and productive.

What Is pytest?

- Definition: Python test framework for writing and executing tests.
- Philosophy: Simple yet powerful, convention over configuration.
- Key Strength: Minimal boilerplate, intuitive syntax.
- Ecosystem: 800+ plugins for extending functionality.
- Adoption: De facto standard for Python testing.

Why pytest Matters

- Simplicity: Write tests as plain functions with assert statements
- Fixtures: Powerful reusable setup/teardown mechanisms
- Parametrization: Test multiple inputs with single test function
- Plugins: Extensive ecosystem covers almost any use case
- Integration: Works with Django, Flask, asyncio, etc.
- Productivity: Less boilerplate = write tests faster
- Debugging: Detailed failure information with pytest output

Key Features

1. Simple Assertions
``python
def test_user():
user = User("Alice", age=30)
assert user.name == "Alice" # Clear, readable
assert user.age == 30
assert user.is_adult() # Works with methods
`

2. Fixtures (Setup & Teardown)
`python
import pytest

@pytest.fixture
def database():
"""Setup: connect to database."""
db = Database()
db.connect()
yield db # Test runs here
db.disconnect() # Teardown

def test_query(database):
"""Fixture automatically injected as parameter."""
result = database.query("SELECT * FROM users")
assert len(result) > 0
`

3. Parametrization
`python
@pytest.mark.parametrize("input,expected", [
(1, 2),
(2, 4),
(3, 6)
])
def test_double(input, expected):
"""Test runs once for each parameter pair."""
assert input * 2 == expected
`

4. Markers & Test Selection
`python
@pytest.mark.slow
def test_slow_operation():
# Long-running test
pass

@pytest.mark.skip(reason="Not implemented")
def test_future():
pass

# Command line: pytest -m "not slow"
`

5. Exception Testing
`python
def test_division_by_zero():
"""Test that exceptions are raised correctly."""
with pytest.raises(ZeroDivisionError):
1 / 0
`

Common Test Patterns

Mocking External Dependencies:
`python
def test_api_call(mocker):
"""Mock external API calls."""
mock_api = mocker.patch("requests.get")
mock_api.return_value.json.return_value = {"status": "ok"}

result = fetch_data()
assert result["status"] == "ok"
`

Temporary Files:
`python
def test_file_processing(tmp_path):
"""pytest provides temporary directories."""
file = tmp_path / "test.txt"
file.write_text("hello")

result = process_file(file)
assert result == "HELLO"
`

Fixtures with Scope:
`python
@pytest.fixture(scope="session")
def expensive_resource():
"""Created once per test session."""
return ExpensiveSetup()

@pytest.fixture(scope="function")
def fresh_db():
"""Created for each test function."""
return Database()
`

Configuration (pytest.ini)
`ini
[pytest]
testpaths = tests
python_files = test_*.py
python_functions = test_*
addopts = -v --tb=short
markers =
slow: marks tests as slow
integration: integration tests
`

Running Tests
`bash
# All tests
pytest

# Specific file
pytest tests/test_user.py

# Specific test
pytest tests/test_user.py::test_login

# With coverage
pytest --cov=myapp

# Parallel execution
pytest -n 4

# Stop on first failure
pytest -x

# Verbose output
pytest -v

# Run marked tests
pytest -m slow
`

Popular Plugins

| Plugin | Purpose |
|--------|---------|
| pytest-cov | Code coverage reports |
| pytest-xdist | Parallel test execution |
| pytest-mock | Enhanced mocking helpers |
| pytest-django | Django testing utilities |
| pytest-asyncio | Async test support |
| pytest-timeout | Test timeout limits |
| pytest-repeat | Repeat tests N times |

Best Practices

1. One assertion per test (usually): Easier to understand failures
2. Descriptive names:
test_login_with_invalid_password better than test_login
3. Arrange-Act-Assert: Setup → Action → Verification
4. Use fixtures: Don't repeat setup code
5. Independent tests: No test should depend on another
6. Mock external deps: Don't call real APIs/database
7. Test behavior, not implementation: Test what it does, not how
8. Parametrize similar tests: Reduce code duplication

pytest vs unittest

| Feature | pytest | unittest |
|---------|--------|----------|
| Syntax | assert statements | self.assertEqual() |
| Fixtures | Powerful, flexible | setUp/tearDown |
| Parametrization | Built-in | Manual loops |
| Plugins | 800+ available | Limited |
| Learning Curve | Gentle | Steeper |
| Enterprise Use | Increasingly standard | Legacy codebases |

Common Assertions
`python
assert x == 5 # Equality
assert x > 0 # Comparison
assert "hello" in text # Membership
assert user is None # Identity
assert callable(func) # Type checking
`

Test Structure Template
`python
import pytest
from mymodule import Calculator

@pytest.fixture
def calc():
return Calculator()

def test_addition(calc):
"""Test that addition works correctly."""
# Arrange
a, b = 2, 3

# Act
result = calc.add(a, b)

# Assert
assert result == 5
``

pytest is the gold standard for Python testing — combining simplicity for beginners with power for advanced users, making it the framework of choice for professionals and open-source projects worldwide.

Want to learn more?

Search 13,225+ semiconductor and AI topics or chat with our AI assistant.

Search Topics Chat with CFSGPT