pytest: zapewniam prawie równe

145

Jak zrobić assert almost equalz py.test dla pływaków bez uciekania się do czegoś takiego:

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

Dokładniej rzecz biorąc, przydatne będzie poznanie zgrabnego rozwiązania do szybkiego porównywania par pływaków bez ich rozpakowywania:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()
Vladimir Keleshev
źródło
3
py.test ma teraz funkcję, która to robi.
dbn
Zobacz tę odpowiedź, aby uzyskać opis tej funkcji
Tom Hale,

Odpowiedzi:

232

Zauważyłem, że to pytanie dotyczyło konkretnie py.test. py.test 3.0 zawiera approx()funkcję (cóż, klasę), która jest bardzo przydatna do tego celu.

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

Dokumentacja jest tutaj: https://docs.pytest.org/en/latest/reference.html#pytest-approx

dbn
źródło
12
Miły! Okazało się również, że działa również dla sekwencji liczb, np.assert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
Pan Kriss
4
@Mr Kriss A nawet dla assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
dyktowania
4
Nie działa to w przypadku list list: na przykład assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]])prowadzi do TypeError. Jeśli okaże się, że Numpy np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(patrz odpowiedź poniżej) zadziałało w tym przypadku.
Kurt Peek,
43

Będziesz musiał określić, co jest dla Ciebie „prawie”:

assert abs(x-y) < 0.0001

zastosowanie do krotek (lub dowolnej sekwencji):

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())
yurib
źródło
3
Pytanie brzmi, jak to zrobić „bez uciekania się do czegoś takiego”
endolith
Interpretuję „coś takiego” jako powtarzające się i niezręczne wyrażenie x - d <= y <= x+d, wydaje się, że właśnie to miał na myśli OP. Jeśli nie chcesz jawnie określać progu dla „prawie”, zobacz odpowiedź @ jiffyclub.
yurib
2
py.test ma teraz funkcję, która to robi. Dodałem odpowiedź omawiającą to.
dbn
2
@NeilG Dlaczego u licha to powinno zostać usunięte? Jeśli jest z nim coś ewidentnie nie tak, proszę wyjaśnij, co to jest.
user2699
1
@ user2699 Pytanie brzmi, jak to zrobić w pytest. Prawidłowym sposobem na zrobienie tego w pytest jest użycie pytest.approx. Pisanie własnej przybliżonej funkcji to zły pomysł. (Ten w tej odpowiedzi nie jest nawet tak dobry, jak ten dołączony.)
Neil G
31

Jeśli masz dostęp do NumPy, ma on świetne funkcje do porównywania zmiennoprzecinkowego, które już wykonują porównania parami numpy.testing.

Następnie możesz zrobić coś takiego:

numpy.testing.assert_allclose(i_return_tuple_of_two_floats(), (1.32, 2.4))
jiffyclub
źródło
11

Coś jak

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

To właśnie robi Unittest

W drugiej części

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

Prawdopodobnie lepiej zawrzeć to w funkcji

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())
John La Rooy
źródło
11

Te odpowiedzi istnieją od dawna, ale myślę, że najłatwiejszym i najbardziej czytelnym sposobem jest użycie unittest, ponieważ zawiera wiele fajnych twierdzeń bez użycia go w strukturze testowej.

Uzyskaj potwierdzenia, zignoruj ​​resztę unittest.TestCase

(na podstawie tej odpowiedzi )

import unittest

assertions = unittest.TestCase('__init__')

Zrób kilka twierdzeń

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

Wykonaj test automatycznego rozpakowywania oryginalnych pytań

Wystarczy użyć *, aby rozpakować zwracaną wartość bez konieczności wprowadzania nowych nazw.

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
KobeJohn
źródło
6

Jeśli chcesz czegoś, co działa nie tylko z liczbami zmiennoprzecinkowymi, ale na przykład dziesiętnymi, możesz użyć Pythona math.isclose:

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

Dokumenty - https://docs.python.org/3/library/math.html#math.isclose

ważne imię
źródło
Tutaj względna tolerancja (lub różnica procentowa) jest wygodna w użyciu w niektórych przypadkach użycia, np. Naukowych.
Karioki
3

Używałbym nosa. Narzędzi. Dobrze współpracuje z py.test runner i ma inne, równie przydatne funkcje - assert_dict_equal (), assert_list_equal () itp.

from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7 
volodymyr
źródło
2
Poza tym pytest ma opcję na to, nie uważam za dobrą opcję dodania dodatkowej zależności (w tym przypadku cała konstrukcja testowa) tylko do tego.
Marc Tudurí