Как сравнивать дробные числа через pytest.approx

Как сравнивать дробные числа через pytest.approx
Содержание статьи

Вычисление 0.1 + 0.2 не обязано точно равняться десятичному 0.3 при двоичном представлении float. Но просто округлить результат до совпадения недостаточно: это может скрыть ошибку. На Mentoring.digital зададим допустимое отклонение через pytest.approx.

Начните с ожидаемого значения и смысла допуска

import pytest

def test_sum():
    assert 0.1 + 0.2 == pytest.approx(0.3)

def test_temperature():
    assert 20.04 == pytest.approx(20.0, abs=0.05)
    assert 20.2 != pytest.approx(20.0, abs=0.05)

В учебном примере температуры мы разрешили отклонение 0.05 единицы измерения. Это условие данного теста, а не универсальная точность любого датчика. Значение 20.04 помещается в допуск, а 20.2 уже нет. Ожидаемый результат расположен внутри approx(), фактический слева от сравнения.

По умолчанию используются относительный допуск 1e-6 и абсолютный 1e-12. Относительный считается от ожидаемого значения. Если передать только abs, относительный допуск отключается. Поэтому явный абсолютный порог в примере означает именно выбранную фиксированную погрешность.

Различайте относительный и абсолютный пороги

import pytest

def test_relative_tolerance():
    assert 1000.5 == pytest.approx(1000.0, rel=0.001, abs=0.01)

def test_near_zero():
    assert 5e-9 == pytest.approx(0.0, abs=1e-8)
    assert 5e-7 != pytest.approx(0.0, abs=1e-8)

В первом тесте относительный порог равен 1.0, абсолютный 0.01. Если указаны оба, достаточно выполнить любой из них: разность 0.5 допускается относительным условием. Это не требование одновременно уложиться в два порога. Рядом с ожидаемым нулём относительный допуск сам по себе не даёт полезного диапазона, поэтому задайте абсолютный.

Не выбирайте допуск по величине уже обнаруженной ошибки только ради зелёного теста. Сначала определите единицы, допустимую погрешность результата и источник приближения. Если задача требует точной десятичной арифметики, например по правилам денежных расчётов, одной замены assert на approx недостаточно.

Наборы сравниваются по соответствующим элементам

import pytest

def test_series():
    result = [0.1 + 0.2, 0.2 + 0.4]
    assert result == pytest.approx([0.3, 0.6], abs=1e-9)

def test_nan_contract():
    missing = float("nan")
    assert missing != pytest.approx(missing)
    assert missing == pytest.approx(missing, nan_ok=True)

Для списка важны длина и порядок соответствующих значений. Множество без установленного порядка для такого сравнения не подходит. Параметр nan_ok=True имеет смысл только там, где NaN разрешён договорённостью о данных. Иначе он способен принять отсутствие результата за допустимое значение.

Сохраните проверки по обе стороны границы

Для своего расчёта подготовьте точное совпадение, отклонение внутри допуска и явное превышение. Добавьте ожидаемый ноль и отсутствующее измерение, если оно допустимо. Так тест подтверждает выбранное правило и сохраняет способность обнаружить ошибку. Используйте approx для равенства, а не вместо операторов порядка.

Подбор проверок можно отработать на практическом курсе тестирования. С ментором по QA полезно обсудить требования к точности до написания числовых assert, приложив небольшой пример расчёта.

Похожие статьи по данной теме