๐Ÿงช Python Tutorial โ€” Unit Testing with pytest

Introduction ๐ŸŒŸ

pytest is the most popular testing framework in Python โ€” simple, powerful, and perfect for both beginners and large-scale applications.

Note

๐Ÿ’ก No boilerplate โ€” just write functions
๐Ÿ’ก Advanced features: fixtures, parametrization, plugins
๐Ÿ’ก Works with CI/CD and large applications

1. Install pytest ๐Ÿ“ฆ

install.sh

pip install pytest

2. Basic Project Structure ๐Ÿงฑ

project_structure.txt

my_project/
โ”‚โ”€โ”€ app/
โ”‚   โ””โ”€โ”€ math_ops.py
โ”‚
โ””โ”€โ”€ tests/
    โ””โ”€โ”€ test_math_ops.py

โœ” pytest auto-discovers files named test_*.py

3. Writing Your First Test โœ…

Code to test (math_ops.py)

math_ops.py

def add(a, b):
    return a + b

Test file (test_math_ops.py)

test_math_ops.py

from app.math_ops import add

def test_add():
    assert add(2, 3) == 5

4. Run pytest โ–ถ๏ธ

run_tests.sh

pytest

โœ” pytest auto-detects and runs all tests

5. Assertions in pytest ๐Ÿง 

assert_examples.py

assert a == b
assert a != b
assert x > 10
assert "hello" in message
assert isinstance(obj, MyClass)

โœ” pytest shows detailed failure info

6. Grouping Tests in Classes ๐Ÿงฉ

test_class.py

class TestMath:
    def test_add(self):
        assert 2 + 3 == 5

    def test_sub(self):
        assert 5 - 2 == 3

Note

๐Ÿ’ก No need for unittest.TestCase

7. Using Fixtures (MOST IMPORTANT) ๐Ÿงฐ

Fixtures provide reusable setup code โ€” used for DB connections, API clients, objects, etc.

fixtures.py

import pytest

@pytest.fixture
def sample_data():
    return {"name": "Sathish", "age": 25"}

def test_name(sample_data):
    assert sample_data["name"] == "Sathish"

โœ” pytest injects fixture into test

8. Multiple Fixtures

multi_fixtures.py

@pytest.fixture
def x(): return 10

@pytest.fixture
def y(): return 20

def test_add(x, y):
    assert x + y == 30

9. Parametrized Tests ๐Ÿ”

parametrize.py

import pytest

@pytest.mark.parametrize("a,b,result", [
    (2, 3, 5),
    (10, 5, 15),
    (-1, 1, 0),
])
def test_add(a, b, result):
    assert a + b == result

โœ” Test multiple inputs easily

10. Expected Exceptions โš ๏ธ

exceptions.py

import pytest

def divide(a, b):
    return a / b

def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        divide(10, 0)

11. Using conftest.py for Global Fixtures ๐Ÿ—‚๏ธ

conftest.py

import pytest

@pytest.fixture
def api_url():
    return "https://api.example.com"

โœ” All tests in the folder can access this fixture

12. Running Specific Tests ๐ŸŽฏ

commands.sh

pytest tests/test_math_ops.py
pytest -k "add"
pytest -m slow

13. Marking Tests (slow, db, network) ๐Ÿท๏ธ

markers.py

import pytest

@pytest.mark.slow
def test_big():
    assert True

run_mark.py

pytest -m slow

14. Code Coverage ๐Ÿ”

coverage_install.sh

pip install pytest-cov

run_coverage.sh

pytest --cov=app --cov-report=term-missing

โœ” Shows how much of your code is tested

15. CI/CD Integration (GitHub Actions) โš™๏ธ

github_actions.yml

name: Python Tests

on: [push]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Install dependencies
      run: pip install -r requirements.txt
    - name: Run tests
      run: pytest

โœ” Automatically runs tests on every push

16. Snapshot Testing (pytest-snapshot) ๐Ÿ“ธ

Useful for testing API responses, JSON, HTML, etc.

snapshot.py

def test_api(snapshot):
    data = {"name": "Sathish", "age": 25}
    snapshot.assert_match(data)

17. Mocking with pytest + unittest.mock ๐Ÿงช

mocking.py

from unittest.mock import patch

@patch("app.api.fetch_data", return_value={"ok": True})
def test_api(mock_fetch):
    from app.api import fetch_data
    assert fetch_data() == {"ok": True}

โœ” Mocking helps test code without real API/DB calls

18. Final Example โ€” Full Test Suite ๐Ÿ“˜

project_example.txt

project/
โ”‚โ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ calc.py
โ”‚   โ””โ”€โ”€ api.py
โ”‚
โ””โ”€โ”€ tests/
    โ”œโ”€โ”€ test_calc.py
    โ”œโ”€โ”€ test_api.py
    โ””โ”€โ”€ conftest.py

Best Practices ๐Ÿ’ก

  • โœ” Keep tests small and independent
  • โœ” Use fixtures for reusable setup
  • โœ” Use parametrization to avoid repetitive tests
  • โœ” Test both success & failure paths
  • โœ” Run tests in CI/CD pipelines
  • โœ” Maintain 80%+ test coverage

Conclusion ๐ŸŽ‰

>>โ€œGood tests act as a safety net โ€” allowing you to change code fearlessly.โ€ โœจ

You now understand Unit Testing with pytest! Want the next topic? Try Mocking, Integration Testing, Test-Driven Development (TDD), or CI/CD Pipelines. Just tell me! ๐Ÿ˜Š