Pytest: asserire quasi uguale


145

Come fare assert almost equalcon py.test per i float senza ricorrere a qualcosa di simile:

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

Più specificamente sarà utile conoscere una soluzione pulita per confrontare rapidamente coppie di float, senza decomprimerle:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()

3
py.test ora ha una funzione che lo fa.
dbn,

Vedi questa risposta per una descrizione di quella caratteristica
Tom Hale,

Risposte:


233

Ho notato che questa domanda è stata posta specificamente su py.test. py.test 3.0 include una approx()funzione (beh, davvero di classe) che è molto utile per questo scopo.

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 documentazione è qui: https://docs.pytest.org/en/latest/reference.html#pytest-approx


12
Bello! Ha anche scoperto che funziona anche per sequenze di numeri, ad esassert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
Mr Kriss,

4
@Mr Kriss E anche per i dicts:assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
Antony Hatchkins,

4
Questo non funziona per gli elenchi di elenchi: ad esempio, assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]])porta a TypeError. Se ho scoperto che Numpy np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(vedi la risposta sotto) ha funzionato per questo caso.
Kurt Peek,

43

Dovrai specificare ciò che è "quasi" per te:

assert abs(x-y) < 0.0001

da applicare alle tuple (o qualsiasi sequenza):

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 domanda chiede come farlo "senza ricorrere a qualcosa del genere"
endolith,

Interpreto "qualcosa del genere" come un'espressione ripetitiva e imbarazzante x - d <= y <= x+d, sembra che sia quello che significava anche OP. Se non si desidera specificare esplicitamente la soglia per "quasi", vedere la risposta di @ jiffyclub.
yurib,

2
py.test ora ha una funzione che lo fa. Ho aggiunto una risposta discutendola.
dbn,

2
@NeilG Perché mai questo dovrebbe essere cancellato? Se c'è qualcosa di ovviamente sbagliato in ciò, ti preghiamo di spiegare di cosa si tratta.
user2699

1
@ user2699 La domanda è come farlo in Pytest. Il modo corretto di farlo in Pytest è usare pytest.approx. Scrivere la tua funzione approssimativa è una cattiva idea. (Quello in questa risposta non è nemmeno buono come quello incluso.)
Neil G

31

Se hai accesso a NumPy ha ottime funzioni per il confronto a virgola mobile che già fanno un confronto a coppie numpy.testing.

Quindi puoi fare qualcosa del tipo:

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

11

Qualcosa di simile a

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

Questo è ciò che fa unittest

Per la seconda parte

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

Probabilmente meglio avvolgerlo in una funzione

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

Queste risposte sono in circolazione da molto tempo, ma penso che il modo più semplice e anche più leggibile sia quello di utilizzare unittest per le sue molte affermazioni belle senza usarlo per la struttura dei test.

Ottieni affermazioni, ignora il resto di unittest.TestCase

(basato su questa risposta )

import unittest

assertions = unittest.TestCase('__init__')

Fai alcune affermazioni

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

Implementare il test di decompressione automatica delle domande originali

Usa * per decomprimere il valore restituito senza dover introdurre nuovi nomi.

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

Se vuoi qualcosa che funzioni non solo con i float ma ad esempio i decimali puoi usare quelli di Python math.isclose:

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

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


Qui la tolleranza relativa (o la differenza percentuale) è conveniente da usare in alcuni casi d'uso, ad es. Scienfific.
Karioki,

3

Userei nose.tools. Gioca bene con py.test runner e ha altri assertori altrettanto utili: assert_dict_equal (), assert_list_equal (), ecc.

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

2
Inoltre pytest ha un'opzione per questo, non considero una buona opzione aggiungere una dipendenza extra (in questo caso, un intero framwork di test) solo per questo.
Marc Tudurí,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.