Skip to Content

Testing with pytest

When to use

Verify behaviour with regression tests in milliseconds, living alongside the code.

Analogy

Tests are a smoke alarm: cheap, automatic, loud when something is wrong.

Data-flow diagram

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

    pytest collects test_*, runs in
    isolation, captures stdout, uses
    fixtures + parametrize; fast (~ms).

Deep explanation

pytest discovers test_*.py and test_* functions; assert statements compare actual vs expected, with rich rewrite. Fixtures (@pytest.fixture) provide setup/teardown. @pytest.mark.parametrize runs the same test over many inputs. Keep tests fast (<100ms each), deterministic (no real network), and isolated (no shared global state).

Examples

Example 1

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

simplest pytest test; assertions rewrite for rich failure output.

Example 2

import pytest
@pytest.fixture
def db():
    import sqlite3
    conn = sqlite3.connect(':memory:')
    yield conn
    conn.close()

def test_insert(db):
    db.execute('CREATE TABLE t (n INT)')
    db.execute('INSERT INTO t VALUES (1)')
    assert db.execute('SELECT n FROM t').fetchone()[0] == 1

fixture provides per-test setup/teardown; yield so teardown runs even on failure.

Example 3

import pytest
@pytest.mark.parametrize('a,b,expect', [
    (1,1,2), (0,0,0), (-1,1,0), (100,200,300),
])
def test_add_param(a, b, expect):
    assert add(a, b) == expect

parametrize reduces 4 tests to 1; pytest reports each row separately.

Common mistake

Tests hitting real external services without a fake - flaky, slow, broken in CI. Wrap services in interfaces; inject fakes for unit tests. Asserting on stdout/print output instead of return values.

Key takeaway

One assertion per concept; permissive fixtures (tmp_path, monkeypatch, capsys); parametrize for combinatorial coverage; markers for slow/integration; keep tests fast and isolated.

Production Failure Playbook

Failure scenario 1: tests-dependent-on-globals

Failure scenario 2: flaky-network-test