pytest: утверждать почти равно

160

Как сделать assert almost equalс py.test для поплавков, не прибегая к чему-то вроде:

assert x - 0.00001 <= y <= x + 0.00001

В частности, будет полезно знать изящное решение для быстрого сравнения пар поплавков, не распаковывая их:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()
Владимир Келешев
источник
3
py.test теперь имеет функцию, которая делает это.
dbn
1
См. Этот ответ для описания этой функции
Том Хейл

Ответы:

256

Я заметил, что этот вопрос конкретно касается py.test. py.test 3.0 включает approx()функцию (ну, действительно класс), которая очень полезна для этой цели.

import pytest

assert 2.2 == pytest.approx(2.3)
# fails, default is ± 2.3e-06
assert 2.2 == pytest.approx(2.3, 0.1)
# passes

# also works the other way, in case you were worried:
assert pytest.approx(2.3, 0.1) == 2.2
# passes

Документация находится здесь: https://docs.pytest.org/en/latest/reference.html#pytest-approx

dbn
источник
14
Ницца! Также обнаружено, что это работает и для последовательностей чисел, напримерassert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
Mr Kriss
4
@Mr Kriss И даже для dicts:assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
Энтони Хэтчкинс
4
Это не работает для списков списков: например, assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]])приводит к TypeError. Если обнаружено, что Numpy's np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(см. Ответ ниже) действительно работает в этом случае.
Курт Пик,
2
Стоит отметить, что второй позиционный аргумент - это относительный допуск, но вы также можете указать абсолютный допуск:0.2 == pytest.approx(0.3, 0.1) # returns false; 0.2 == pytest.approx(0.3, abs=0.1) # returns true
Кайл,
43

Вам нужно будет указать, что для вас «почти»:

assert abs(x-y) < 0.0001

для применения к кортежам (или любой последовательности):

def almost_equal(x,y,threshold=0.0001):
  return abs(x-y) < threshold

assert all(map(almost_equal, zip((1.32, 2.4), i_return_tuple_of_two_floats())
Юриб
источник
3
Вопрос спрашивает, как это сделать, «не прибегая к чему-то вроде этого»
endolith
Я интерпретирую «что-то вроде этого» как повторяющееся и неловкое выражение вроде x - d <= y <= x+d, похоже, это то, что имел в виду OP. Если вы не хотите явно указывать порог для «почти», см. Ответ @ jiffyclub.
yurib
2
py.test теперь имеет функцию, которая делает это. Я добавил ответ, обсуждая это.
dbn 03
2
@NeilG Зачем это нужно удалять? Если что-то явно не так, пожалуйста, объясните, в чем дело.
user2699 05
1
@ user2699 Вопрос в том, как это сделать в pytest. Правильный способ сделать это в pytest - использовать pytest.approx. Писать собственную приближенную функцию - плохая идея. (Тот, что в этом ответе, даже не так хорош, как включенный.)
Neil G
32

Если у вас есть доступ к NumPy, у него есть отличные функции для сравнения с плавающей запятой, которые уже выполняют попарное сравнение с numpy.testing.

Тогда вы можете сделать что-то вроде:

numpy.testing.assert_allclose(i_return_tuple_of_two_floats(), (1.32, 2.4))
jiffyclub
источник
12

Эти ответы существуют уже давно, но я думаю, что самый простой и наиболее читаемый способ - использовать unittest для множества хороших утверждений, не используя его для структуры тестирования.

Получать утверждения, игнорировать остальную часть unittest.TestCase

(на основе этого ответа )

import unittest

assertions = unittest.TestCase('__init__')

Сделайте некоторые утверждения

x = 0.00000001
assertions.assertAlmostEqual(x, 0)  # pass
assertions.assertEqual(x, 0)  # fail
# AssertionError: 1e-08 != 0

Реализовать тест автоматической распаковки оригинальных вопросов

Просто используйте *, чтобы распаковать возвращаемое значение без необходимости вводить новые имена.

i_return_tuple_of_two_floats = lambda: (1.32, 2.4)
assertions.assertAlmostEqual(*i_return_tuple_of_two_floats())  # fail
# AssertionError: 1.32 != 2.4 within 7 places
КобиДжон
источник
11

Что-то типа

assert round(x-y, 5) == 0

Вот что делает unittest

Для второй части

assert all(round(x-y, 5) == 0 for x,y in zip((1.32, 2.4), i_return_tuple_of_two_floats()))

Наверное, лучше обернуть это функцией

def tuples_of_floats_are_almost_equal(X, Y):
    return all(round(x-y, 5) == 0 for x,y in zip(X, Y))

assert tuples_of_floats_are_almost_equal((1.32, 2.4), i_return_tuple_of_two_floats())
Джон Ла Рой
источник
7

Если вам нужно что-то, что работает не только с числами с плавающей запятой, но, например, с десятичными знаками, вы можете использовать python math.isclose:

    # - rel_tol=0.01` is 1% difference tolerance.
    assert math.isclose(actual_value, expected_value, rel_tol=0.01)

Документы - https://docs.python.org/3/library/math.html#math.isclose

действительное имя
источник
Здесь относительный допуск (или процентная разница) удобно использовать в некоторых случаях использования, например, в научных.
Karioki
3

Я бы использовал нос. Он хорошо работает с бегуном py.test и имеет другие не менее полезные утверждения - assert_dict_equal (), assert_list_equal () и т. Д.

from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7 
Владимир
источник
2
Кроме того, в pytest есть опция для этого, я не считаю хорошим вариантом добавить дополнительную зависимость (в данном случае - целую структуру тестирования) только для этого.
Marc Tudurí