Pytest is a powerful Python testing framework that is widely used for unit testing, integration testing, and functional testing. This guide will take you step by step from basic to advanced features of Pytest, providing a comprehensive understanding of its capabilities.
- Introduction to Pytest
- Setting Up Pytest
- Writing Your First Test
- Test Discovery
- Assertions in Pytest
- Using Fixtures
- Parameterized Testing
- Marking Tests
- Running Tests with Command-Line Options
- Pytest Plugins
- Mocking with Pytest
- Working with Pytest.ini and Configuration
- Advanced Features of Pytest
- Best Practices for Writing Tests
What is Pytest?
- Pytest is a Python testing framework that makes it easy to write small, scalable, and highly maintainable test cases.
- It supports fixtures, parameterized testing, and plugins to extend its capabilities.
Why Use Pytest?
- Simple syntax for writing tests.
- Automatic test discovery.
- Rich ecosystem of plugins.
- Built-in support for fixtures and mocking.
-
Install Pytest
pip install pytest
-
Verify Installation
pytest --version
-
Directory Structure
- Example structure for a basic Pytest project:
my_project/ ├── src/ │ └── my_code.py └── tests/ └── test_my_code.py
- Example structure for a basic Pytest project:
Example Code:
-
Create a Python file
src/my_code.py
:def add(a, b): return a + b
-
Create a test file
tests/test_my_code.py
:from src.my_code import add def test_add(): assert add(1, 2) == 3 assert add(-1, 1) == 0
Run the Test:
pytest tests/test_my_code.py
- Pytest automatically discovers tests by looking for files starting with
test_
or ending with_test.py
. - Test functions must start with
test_
.
Run All Tests:
pytest
- Pytest uses Python's
assert
keyword for making assertions. - Examples:
def test_example(): assert 1 + 1 == 2 assert "pytest" in "Learning pytest is fun!" assert {"key": "value"} == {"key": "value"}
Fixtures are used to set up and tear down the test environment.
Basic Fixture Example:
import pytest
@pytest.fixture
def sample_data():
return [1, 2, 3]
def test_data(sample_data):
assert len(sample_data) == 3
assert sum(sample_data) == 6
Parameterization allows you to run the same test with different inputs.
Example:
import pytest
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(-1, -1, -2),
(0, 0, 0),
])
def test_add(a, b, expected):
assert a + b == expected
Markers help you organize and run specific groups of tests.
Using Markers:
import pytest
@pytest.mark.slow
def test_slow_function():
import time
time.sleep(5)
assert True
Run Tests with Specific Markers:
pytest -m slow
- Run Verbosely:
pytest -v
- Stop on First Failure:
pytest -x
- Generate Test Report:
pytest --html=report.html
Popular Pytest Plugins:
pytest-html
: Generate HTML reports.pytest-mock
: Mocking library.pytest-cov
: Code coverage reports.
Install a Plugin:
pip install pytest-html
Usage:
pytest --html=report.html
Mock external dependencies using pytest-mock
.
Example:
from unittest.mock import MagicMock
def test_mock(mocker):
mock_function = mocker.patch("src.my_code.external_function")
mock_function.return_value = 42
assert mock_function() == 42
- Create a
pytest.ini
file:[pytest] markers = slow: marks tests as slow addopts = --maxfail=3 -v
-
Running Tests in Parallel:
- Install
pytest-xdist
:pip install pytest-xdist
- Run tests:
pytest -n 4
- Install
-
Code Coverage:
- Install
pytest-cov
:pip install pytest-cov
- Run tests with coverage:
pytest --cov=src
- Install
-
Custom Fixtures:
@pytest.fixture(scope="module") def db_connection(): conn = setup_database_connection() yield conn conn.close()
- Keep tests simple and focused.
- Use meaningful test names.
- Avoid hardcoding values; use fixtures and parameterization.
- Run tests frequently during development.
- Use CI/CD pipelines to automate test execution.
This step-by-step guide covers everything you need to get started with Pytest and progress to advanced testing capabilities. Pytest is a versatile tool that can handle a variety of testing scenarios, from simple unit tests to complex integration tests. By mastering Pytest, you can ensure the reliability and quality of your Python codebase.
Let me know if you'd like detailed examples or further elaboration on any specific section!