pytest: стверджувати майже рівно


145

Як зробити assert almost equalpy.test для floats, не вдаючись до чогось подібного:

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

Більш конкретно, буде корисно знати акуратне рішення для швидкого порівняння пар float, не розпаковуючи їх:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()

3
py.test тепер має функцію, яка це робить.
дбн

Дивіться цю відповідь для опису цієї функції
Том Хейл

Відповіді:


232

Я помітив, що це питання спеціально задавали про 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


12
Приємно! Також виявлено, що він працює і для послідовностей чисел, наприкладassert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
Містер Крісс

4
@Mr Kriss І навіть для диктів: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 np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(див. Відповідь нижче) працював у цій справі.
Курт Пік

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
Питання задає питання, як це зробити ", не вдаючись до чогось подібного", це
ендоліт

Я трактую "щось подібне" як повторюваний і незграбний вираз на зразок x - d <= y <= x+d, схоже, саме це означало і ОП. Якщо ви не бажаєте чітко вказати поріг "майже", див. Відповідь @ jiffyclub.
лютого

2
py.test тепер має функцію, яка це робить. Я додав відповідь, обговорюючи її.
dbn

2
@NeilG Чому на Землі це слід видалити? Якщо з цим явно щось не так, поясніть, що це таке.
користувач2699

1
@ user2699 Питання в тому, як це зробити в pytest. Правильний спосіб зробити це в pytest - це використовувати pytest.approx. Написати власну приблизну функцію - погана ідея. (Той, хто відповідає у цій відповіді, навіть не такий хороший, як включений.)
Ніл Г

31

Якщо у вас є доступ до NumPy, він має чудові функції порівняння з плаваючою комою, які вже роблять попарне порівняння з numpy.testing.

Тоді ви можете зробити щось на кшталт:

numpy.testing.assert_allclose(i_return_tuple_of_two_floats(), (1.32, 2.4))

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())

11

Ці відповіді існують вже давно, але я думаю, що найпростіший, а також найчитабніший спосіб - це використовувати 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

6

Якщо ви хочете щось, що працює не тільки з поплавцями, але, наприклад, з десятковими знаками, ви можете використовувати python's 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


Тут відносна толерантність (або відсоткова різниця) зручно використовувати в деяких випадках використання, наприклад, наукових.
Каріокі

3

Я б використовував nos.tools. Він добре грає з py.test runner і має інші не менш корисні твердження - 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 є варіант для цього, я не вважаю хорошим варіантом додати додаткову залежність (в даному випадку цілий тестовий фрагмент) лише для цього.
Марк Тудурі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.