pytest: fast gleich behaupten

144

So geht's assert almost equalmit py.test für Floats, ohne auf Folgendes zurückzugreifen:

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

Insbesondere ist es nützlich, eine übersichtliche Lösung zu kennen, um Schwimmerpaare schnell zu vergleichen, ohne sie auszupacken:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()
Vladimir Keleshev
quelle
3
py.test hat jetzt eine Funktion, die dies tut.
dbn
Siehe diese Antwort für eine Beschreibung dieser Funktion
Tom Hale

Antworten:

232

Mir ist aufgefallen, dass diese Frage speziell zu py.test gestellt wurde. py.test 3.0 enthält eine approx()Funktion ( na ja , wirklich Klasse), die für diesen Zweck sehr nützlich ist.

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

Die Dokumentation finden Sie hier: https://docs.pytest.org/en/latest/reference.html#pytest-approx

dbn
quelle
12
Nett! Fand auch, dass es auch für Zahlenfolgen funktioniert, zBassert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
Mr Kriss
4
@ Herr Kriss Und sogar für Diktate:assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
Antony Hatchkins
4
Dies funktioniert nicht für Listen von Listen: assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]])führt beispielsweise zu a TypeError. Wenn festgestellt wird, dass Numpy's np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(siehe Antwort unten) für diesen Fall funktioniert hat.
Kurt Peek
43

Sie müssen angeben, was für Sie "fast" ist:

assert abs(x-y) < 0.0001

auf Tupel (oder eine beliebige Sequenz) anwenden:

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
quelle
3
Die Frage fragt, wie es geht, "ohne auf so etwas zurückzugreifen"
Endolith
Ich interpretiere "so etwas" als einen sich wiederholenden und unangenehmen Ausdruck x - d <= y <= x+d, der so aussieht, als ob OP dies auch bedeutete. Wenn Sie den Schwellenwert für "fast" nicht explizit angeben möchten, lesen Sie die Antwort von @ jiffyclub.
Yurib
2
py.test hat jetzt eine Funktion, die dies tut. Ich habe eine Antwort hinzugefügt, die darüber diskutiert.
dbn
2
@NeilG Warum um alles in der Welt sollte dies gelöscht werden? Wenn etwas offensichtlich nicht stimmt, erklären Sie bitte, was es ist.
user2699
1
@ user2699 Die Frage ist, wie dies in pytest gemacht wird. Der richtige Weg, dies im Pytest zu tun, ist die Verwendung pytest.approx. Es ist eine schlechte Idee, eine eigene ungefähre Funktion zu schreiben. (Die in dieser Antwort ist nicht einmal so gut wie die enthaltene.)
Neil G
31

Wenn Sie Zugriff auf NumPy haben, bietet es großartige Funktionen für den Gleitkomma-Vergleich, mit denen bereits ein paarweiser Vergleich durchgeführt wird numpy.testing.

Dann können Sie so etwas tun wie:

numpy.testing.assert_allclose(i_return_tuple_of_two_floats(), (1.32, 2.4))
Jiffyclub
quelle
11

Etwas wie

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

Das ist es, was Unittest tut

Für den zweiten Teil

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

Wahrscheinlich ist es besser, das in eine Funktion zu packen

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
quelle
11

Diese Antworten gibt es schon lange, aber ich denke, der einfachste und auch am besten lesbare Weg ist, unittest für seine vielen netten Behauptungen zu verwenden, ohne es für die Teststruktur zu verwenden.

Holen Sie sich Behauptungen, ignorieren Sie den Rest von unittest.TestCase

(basierend auf dieser Antwort )

import unittest

assertions = unittest.TestCase('__init__')

Machen Sie einige Aussagen

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

Implementieren Sie den Test zum automatischen Auspacken der Originalfragen

Verwenden Sie einfach *, um Ihren Rückgabewert zu entpacken, ohne neue Namen eingeben zu müssen.

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
quelle
6

Wenn Sie etwas möchten, das nicht nur mit Floats, sondern auch mit Dezimalstellen funktioniert, können Sie Pythons verwenden math.isclose:

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

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

gültiger Name
quelle
Hier ist die relative Toleranz (oder der prozentuale Unterschied) in einigen Anwendungsfällen, z. B. wissenschaftlich, zweckmäßig.
Karioki
3

Ich würde nas.tools verwenden. Es spielt sich gut mit py.test Runner und hat andere ebenso nützliche Asserts - assert_dict_equal (), assert_list_equal () usw.

from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7 
volodymyr
quelle
2
Abgesehen davon, dass pytest eine Option dafür hat, halte ich es nicht für eine gute Option, eine zusätzliche Abhängigkeit (in diesem Fall ein ganzes Test-Framwork) nur dafür hinzuzufügen.
Marc Tudurí