Python Testing

Tutorial 61 of 65 · pythondeck.com Python course

The standard unittest module ships with Python; pytest is the de-facto third-party framework. Tests are functions whose names start with test_. Use assertions, fixtures and parametrisation.

Automated tests document behavior, catch regressions, and enable refactoring. pytest is the de facto standard in Python with fixtures, parametrization, and rich plugin ecosystem.

Testing discipline separates prototypes from software you can ship and hand to another developer confidently.

A failing test should pinpoint the regression in minutes—vague assertions and shared fixtures across unrelated modules slow everyone down.

pytest basics — assert, test discovery, -k filters.

fixtures — setup/teardown with scope function/class/session.

parametrize — table-driven cases for edge inputs.

mocking — unittest.mock or pytest-mock for boundaries (HTTP, clock).

coverage — pytest-cov highlights untested branches, not quality alone.

CI — run tests on every PR; fail on warnings you care about.

Test pyramid: many fast unit tests, fewer integration tests hitting real DB in containers, minimal brittle full-browser E2E. Property-based testing (hypothesis) finds edge cases for parsers and serializers. Freeze time with freezegun for billing logic.

Flaky tests erode trust—quarantine and fix root cause (race, shared global state). Use factories (factory_boy) over huge fixture YAML.

Contract tests between services (pact or schema snapshots) catch API drift before integration environments do.

xdist parallelizes slow integration suites when tests are isolated and database fixtures use transactions.

Testing implementation details instead of public behavior.

Shared mutable globals between tests causing order dependence.

No tests for failure paths—only happy path assert True.

Chasing 100% coverage while missing assertion on outcomes.

One logical assert concept per test; descriptive names (test_refund_when_insufficient_balance).

Mark slow tests @pytest.mark.slow; default CI runs fast suite.

Use tmp_path fixture for filesystem side effects.

Pair bug fixes with regression test reproducing the issue.

Snapshot HTTP responses for external APIs; refresh snapshots only in reviewed PRs.

Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.

The program below demonstrates unittest. Read the comments on each line, run the code, then change names or values to see how the output shifts.

# Example: unittest
# Run in the REPL or save as a .py file and execute with python.
import unittest
def add(a, b): return a + b

class TestAdd(unittest.TestCase):
    def test_int(self):
        self.assertEqual(add(2, 3), 5)
    def test_str(self):
        self.assertEqual(add("a", "b"), "ab")

if __name__ == "__main__":
    unittest.main()

This sample walks through pytest in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.

# test_math.py
def add(a, b): return a + b

def test_int(): assert add(2, 3) == 5
def test_neg(): assert add(-1, -1) == -2

# run:  pytest

Here is a hands-on illustration of fixture. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.

# Example: Fixture
# Run in the REPL or save as a .py file and execute with python.
import pytest

@pytest.fixture
def sample():
    return [1, 2, 3]

def test_sum(sample):
    assert sum(sample) == 6

The program below demonstrates pytest basics. Read the comments on each line, run the code, then change names or values to see how the output shifts.

# pytest discovers test_*.py and assert statements
def add(a, b):  # function under test
    return a + b  # implementation

def test_add():  # test function
    assert add(2, 3) == 5  # expectation

def test_add_negative():  # another case
    assert add(-1, 1) == 0  # edge case
# Run: pytest -q this_file.py
print("tests defined — run pytest separately")  # note

This sample walks through pytest parametrize in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.

# pytest.mark.parametrize runs many inputs through one test
import pytest  # pytest markers

@pytest.mark.parametrize("a,b,expected", [(1,2,3), (0,0,0), (-1,1,0)])  # table
def test_add_table(a, b, expected):  # parametrized test
    assert a + b == expected  # single assertion

def double(x):  # another function
    return x * 2  # times two

@pytest.mark.parametrize("x", [0, 2, 5])  # inputs only
def test_double(x):  # verify doubling
    assert double(x) == x * 2  # property check

« Python Flet All tutorials Python Logging »