pytest: afirmar casi igual

145

Cómo hacer assert almost equalcon py.test para flotadores sin recurrir a algo como:

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

Más específicamente, será útil conocer una solución ordenada para comparar rápidamente pares de flotadores, sin desempaquetarlos:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()
Vladimir Keleshev
fuente
3
py.test ahora tiene una función que hace esto.
dbn
Vea esta respuesta para obtener una descripción de esa característica
Tom Hale,

Respuestas:

232

Me di cuenta de que esta pregunta era específicamente sobre py.test. py.test 3.0 incluye una approx()función (bueno, realmente de clase) que es muy útil para este propósito.

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 documentación está aquí: https://docs.pytest.org/en/latest/reference.html#pytest-approx

dbn
fuente
12
¡Agradable! También se encontró que funciona para secuencias de números también, por ejemploassert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
Mr Kriss
44
@ Mr Kriss E incluso para los dictados:assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
Antony Hatchkins
44
Esto no funciona para listas de listas: por ejemplo, assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]])conduce a a TypeError. Si se encuentra que Numpy's np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(ver la respuesta a continuación) funcionó para este caso.
Kurt Peek
43

Tendrá que especificar qué es "casi" para usted:

assert abs(x-y) < 0.0001

para aplicar a las tuplas (o cualquier secuencia):

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
fuente
3
La pregunta pregunta cómo hacerlo "sin recurrir a algo como" esto
endolito
Interpreto "algo así" como una expresión repetitiva y extraña x - d <= y <= x+d, parece que eso es lo que OP también quiso decir. Si no desea especificar explícitamente el umbral para 'casi', consulte la respuesta de @ jiffyclub.
yurib
2
py.test ahora tiene una función que hace esto. He agregado una respuesta discutiéndola.
dbn
2
@NeilG ¿Por qué demonios se debe eliminar esto? Si obviamente hay algo mal, explique de qué se trata.
user2699
1
@ user2699 La pregunta es cómo hacer esto en pytest. La forma correcta de hacerlo en pytest es usarlo pytest.approx. Escribir su propia función aproximada es una mala idea. (El de esta respuesta ni siquiera es tan bueno como el incluido.)
Neil G
31

Si tiene acceso a NumPy, tiene excelentes funciones para la comparación de coma flotante con las que ya se realiza la comparación por pares numpy.testing.

Entonces puedes hacer algo como:

numpy.testing.assert_allclose(i_return_tuple_of_two_floats(), (1.32, 2.4))
jiffyclub
fuente
11

Algo como

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

Eso es lo que hace unittest

Para la segunda parte

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

Probablemente mejor envolver eso en una función

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

Estas respuestas han existido durante mucho tiempo, pero creo que la forma más fácil y fácil de leer es usar unittest porque son muchas afirmaciones agradables sin usarlo para la estructura de prueba.

Obtenga aserciones, ignore el resto de unittest.

(basado en esta respuesta )

import unittest

assertions = unittest.TestCase('__init__')

Hacer algunas afirmaciones

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

Implemente la prueba de desempaque automático de las preguntas originales

Simplemente use * para desempaquetar su valor de retorno sin necesidad de introducir nuevos nombres.

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

Si desea algo que funcione no solo con flotadores, sino también con decimales, puede usar Python math.isclose:

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

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

nombre válido
fuente
Aquí la tolerancia relativa (o la diferencia porcentual) es conveniente de usar en algunos casos de uso, por ejemplo, científico.
Karioki
3

Yo usaría nose.tools. Funciona bien con py.test runner y tiene otras afirmaciones igualmente útiles: afirmar_dict_equal (), afirmar_lista_equal (), etc.

from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7 
volodymyr
fuente
2
Además de que pytest tiene una opción para esto, no considero que sea una buena opción agregar una dependencia adicional (en este caso, un marco de prueba completo) solo para esto.
Marc Tudurí