pytest: affirmer presque égal


145

Comment faire assert almost equalavec py.test pour les flotteurs sans recourir à quelque chose comme:

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

Plus précisément il sera utile de connaître une solution soignée pour comparer rapidement des paires de flotteurs, sans les déballer:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()

3
py.test a maintenant une fonctionnalité qui fait cela.
dbn le

Voir cette réponse pour une description de cette fonctionnalité
Tom Hale

Réponses:


233

J'ai remarqué que cette question posait spécifiquement sur py.test. py.test 3.0 inclut une approx()fonction (enfin, vraiment classe) qui est très utile à cette fin.

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

La documentation est ici: https://docs.pytest.org/en/latest/reference.html#pytest-approx


12
Agréable! Aussi trouvé que cela fonctionne aussi pour les séquences de nombres, par exempleassert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
M. Kriss

4
@Mr Kriss Et même pour les dicts:assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
Antony Hatchkins

4
Cela ne fonctionne pas pour les listes de listes: par exemple, assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]])conduit à un fichier TypeError. Si trouvé que Numpy np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(voir la réponse ci-dessous) a fonctionné pour ce cas.
Kurt Peek

43

Vous devrez spécifier ce qui est "presque" pour vous:

assert abs(x-y) < 0.0001

pour appliquer aux tuples (ou à toute séquence):

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
La question demande comment le faire "sans recourir à quelque chose comme" ceci
endolith

J'interprète "quelque chose comme ça" comme une expression répétitive et maladroite comme x - d <= y <= x+d, semble que c'est ce que OP voulait dire aussi. Si vous ne souhaitez pas spécifier explicitement le seuil pour «presque», consultez la réponse de @ jiffyclub.
yurib

2
py.test a maintenant une fonctionnalité qui fait cela. J'ai ajouté une réponse en discutant.
dbn

2
@NeilG Pourquoi diable devrait-il être supprimé? S'il y a manifestement quelque chose qui ne va pas, veuillez expliquer de quoi il s'agit.
user2699

1
@ user2699 La question est de savoir comment faire cela dans pytest. La bonne façon de le faire dans pytest est d'utiliser pytest.approx. Ecrire votre propre fonction approximative est une mauvaise idée. (Celui de cette réponse n'est même pas aussi bon que celui inclus.)
Neil G

31

Si vous avez accès à NumPy, il dispose d'excellentes fonctions de comparaison en virgule flottante qui font déjà une comparaison par paires avec numpy.testing.

Ensuite, vous pouvez faire quelque chose comme:

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

11

Quelque chose comme

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

C'est ce que unittest - t

Pour la deuxième partie

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

Probablement mieux d'envelopper ça dans une fonction

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

Ces réponses existent depuis longtemps, mais je pense que le moyen le plus simple et le plus lisible est d'utiliser unittest pour ses nombreuses affirmations. sans l'utiliser pour la structure de test.

Obtenez des assertions, ignorez le reste de unittest.

(basé sur cette réponse )

import unittest

assertions = unittest.TestCase('__init__')

Faites quelques affirmations

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

Mettre en œuvre le test de décompression automatique des questions originales

Utilisez simplement * pour décompresser votre valeur de retour sans avoir à introduire de nouveaux noms.

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

Si vous voulez quelque chose qui fonctionne non seulement avec les flottants mais par exemple Decimals, vous pouvez utiliser python math.isclose:

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

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


Ici, la tolérance relative (ou la différence en pourcentage) est pratique à utiliser dans certains cas d'utilisation, par exemple scienfific.
Karioki

3

J'utiliserais des outils nasaux. Il joue bien avec py.test runner et a d'autres assertions tout aussi utiles - assert_dict_equal (), assert_list_equal (), etc.

from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7 

2
En plus pytest a une option pour cela, je ne considère pas comme une bonne option d'ajouter une dépendance supplémentaire (dans ce cas, un cadre de test complet) uniquement pour cela.
Marc Tudurí
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.