Assertions and approx

pytest · Essentials · 8 min

Clear failure output is what makes a failing test quick to fix, and float comparison is a classic source of flaky tests.

pytest rewrites assert statements in test modules so that a failing assert shows the values involved. assert result == expected on two dicts prints which keys differ; on lists, the first differing index; on long strings, a diff. You get this from a bare assert, so there's no need for special assertion methods.

You can add a message as the second operand, assert x > 0, "balance must be positive", shown alongside the introspection.

Floating point arithmetic is inexact: 0.1 + 0.2 == 0.3 is False. Use pytest.approx:

  • assert 0.1 + 0.2 == pytest.approx(0.3) uses a relative tolerance of 1e-6 by default.
  • approx(value, rel=1e-3) or abs=0.01 sets the tolerance.
  • It works on lists, tuples, dicts and NumPy arrays: assert result == approx([0.33, 0.67], abs=0.01).

Some patterns to avoid:

  • assert (x, "message") with parentheses: that's a non-empty tuple, always true. pytest warns about it.
  • Very long boolean expressions: split them into several asserts so the failure points at the actual problem.
  • Comparing against True with ==; just assert is_valid(x).

Assertion rewriting only applies to test modules and conftest files by default; helper modules need registration, as covered in the internals lesson.

Example

# test_stats.py
import pytest

def mean(xs):
    return sum(xs) / len(xs)

def test_mean_float():
    assert mean([0.1, 0.2]) == pytest.approx(0.15)

def test_split():
    assert [x / 3 for x in (1, 2)] == pytest.approx([0.333, 0.667], abs=1e-3)

def test_report():
    got = {"n": 3, "mean": 2.0, "max": 4}
    assert got == {"n": 3, "mean": 2.0, "max": 5}

# $ pytest -q
# E   AssertionError: assert {'n': 3, 'mean': 2.0, 'max': 4} == {'n': 3, 'mean': 2.0, 'max': 5}
# E     Omitting 2 identical items, use -vv to show
# E     Differing items:
# E     {'max': 4} != {'max': 5}
# 1 failed, 2 passed

Remember

  • Plain assert gives rich failure diffs thanks to assertion rewriting
  • Use pytest.approx for floats, with rel= or abs= tolerances
  • approx works on sequences and dicts too
  • Never wrap assert and its message in parentheses

Watch out for

  • Writing assert (a == b, 'msg'), which always passes.

Check yourself

  1. Why does `assert 0.1 + 0.2 == 0.3` fail?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pytest.org/en/stable/how-to/assert.html