Python24 min read

Python Testing with Pytest

Learn pytest to verify your code automatically, catch bugs early, and ship confidently using fixtures, parametrized tests, and mocking.

David Miller
September 12, 2025
4.2k177

Testing means: you write code that checks your code.

        Why it matters:
        - it prevents bugs
        - it protects you when you refactor
        - it makes your project trustworthy for teams and clients
        
        Pytest is popular because it is:
        - simple syntax
        - powerful features
        - easy to read and maintain
        
        ## Install pytest
        
        ```bash
        pip install pytest
        ```
        
        ## Your first test (the pytest style)
        
        Create a file named: `test_math.py`
        
        ```python
        def add(a, b):
            return a + b
        
        def test_add():
            assert add(2, 3) == 5
            assert add(0, 0) == 0
            assert add(-1, 1) == 0
        ```
        
        Run:
        ```bash
        pytest
        ```
        
        ### What is assert?
        `assert` means: “This must be true.”  
        If false, the test fails.
        
        ## Test a class
        
        ```python
        class Calculator:
            def add(self, a, b):
                return a + b
        
            def multiply(self, a, b):
                return a * b
        
        def test_calculator():
            calc = Calculator()
            assert calc.add(2, 3) == 5
            assert calc.multiply(2, 3) == 6
        ```
        
        ## Fixtures (setup for tests)
        
        Fixtures are reusable setup functions.
        
        ```python
        import pytest
        
        @pytest.fixture
        def sample_data():
            return [1, 2, 3, 4, 5]
        
        def test_sum(sample_data):
            assert sum(sample_data) == 15
        
        def test_length(sample_data):
            assert len(sample_data) == 5
        ```
        
        Benefit:
        - no repeated setup code
        - cleaner tests
        
        ## Parametrize (test many cases quickly)
        
        ```python
        import pytest
        
        def is_even(n):
            return n % 2 == 0
        
        @pytest.mark.parametrize("number,expected", [
            (2, True),
            (3, False),
            (4, True),
            (5, False)
        ])
        def test_is_even(number, expected):
            assert is_even(number) == expected
        ```
        
        This is excellent for:
        - edge cases
        - boundary values
        - input variations
        
        ## Testing exceptions
        
        ```python
        import pytest
        
        def divide(a, b):
            if b == 0:
                raise ValueError("Cannot divide by zero")
            return a / b
        
        def test_divide_by_zero():
            with pytest.raises(ValueError):
                divide(10, 0)
        ```
        
        ## Mocking (when external dependency exists)
        
        Mocking helps when your function depends on:
        - API calls
        - database
        - payment gateway
        - file system
        
        ```python
        from unittest.mock import Mock
        
        def get_user_data(api):
            return api.fetch_user(123)
        
        def test_get_user_data():
            mock_api = Mock()
            mock_api.fetch_user.return_value = {"name": "Tom"}
        
            result = get_user_data(mock_api)
            assert result["name"] == "Tom"
        ```
        
        ## Graph: testing flow
        
        ```mermaid
        flowchart LR
          A[Write feature code] --> B[Write tests]
          B --> C[Run pytest]
          C --> D{All tests pass?}
          D -->|Yes| E[Ship confidently]
          D -->|No| F[Fix bug + rerun]
        ```
        
        ## Best practices (student-friendly)
        
        - Write tests for your core logic first
        - Add edge case tests (empty lists, None, zero, negative)
        - Keep tests independent
        - Use fixtures for setup
        - Mock external services
        
        ## Remember
        
        - Tests reduce bugs and fear of change
        - pytest is simple: function name starts with test_
        - Use fixtures for setup, parametrize for many cases
        
#Python#Advanced#Testing