pytest: afirmar casi igual

4 minutos de lectura

avatar de usuario
vladimir keleshev

Cómo hacer assert almost equal con 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 buena solución para comparar rápidamente pares de flotadores, sin desempaquetarlos:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()

  • py.test ahora tiene una característica que hace esto.

    – dbn

    21 de septiembre de 2016 a las 18:06

  • Ver esta respuesta para una descripción de esa característica

    – Tom Hale

    10 de noviembre de 2017 a las 4:52


avatar de usuario
dbn

Me di cuenta de que esta pregunta se refería específicamente a py.test. py.test 3.0 incluye un approx() función (bueno, realmente 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 es aquí.

  • ¡Agradable! También descubrí que también funciona para secuencias de números, por ejemplo assert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])

    – Señor Kriss

    16 de marzo de 2017 a las 11:46

  • @Mr Kriss E incluso para dictados: assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})

    –Antony Hatchkins

    17 abr 2017 a las 18:56

  • 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 un 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

    12/09/2017 a las 10:35

  • Vale la pena señalar que el segundo argumento posicional es la tolerancia relativa, pero también puede especificar la tolerancia absoluta: 0.2 == pytest.approx(0.3, 0.1) # returns false; 0.2 == pytest.approx(0.3, abs=0.1) # returns true

    – Kyle

    12 de agosto de 2020 a las 17:01


  • Yo creo que es [now] Una función. def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:

    – Felipe Álvarez

    19 de enero a las 4:51

avatar de usuario
yurib

Tendrás que especificar qué es “casi” para ti:

assert abs(x-y) < 0.0001

para aplicar a 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())

  • La pregunta pregunta cómo hacerlo “sin recurrir a algo como” esto

    – endolito

    28 de agosto de 2016 a las 2:09

  • Interpreto “algo así” como una expresión repetitiva e incómoda como 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

    12 de octubre de 2016 a las 12:22

  • py.test ahora tiene una característica que hace esto. He agregado una respuesta discutiéndolo.

    – dbn

    3 de noviembre de 2016 a las 18:36

  • @NeilG ¿Por qué demonios debería eliminarse esto? Si hay algo obviamente mal con él por favor explique lo que es.

    – usuario2699

    5 de enero de 2018 a las 21:50

  • @ user2699 La pregunta es cómo hacer esto en pytest. La forma correcta de hacerlo en pytest es usar pytest.approx. Escribir tu propia función aproximada es una mala idea. (El de esta respuesta ni siquiera es tan bueno como el incluido).

    – Neil G.

    5 de enero de 2018 a las 22:43

avatar de usuario
jiffyclub

Si tiene acceso a NumPy, tiene excelentes funciones para la comparación de puntos flotantes que ya hacen comparaciones por pares con numpy.testing.

Entonces puedes hacer algo como:

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

avatar de usuario
KobeJohn

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

Obtenga afirmaciones, ignore el resto de unittest.TestCase

(basado en esta respuesta)

import unittest

assertions = unittest.TestCase('__init__')

Haz algunas afirmaciones

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

Implementar la prueba de desempaquetado 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

avatar de usuario
Juan La Rooy

Algo como

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

Eso es lo que prueba de unidad lo hace

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 sea 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())

Si desea algo que funcione no solo con flotantes sino también con decimales, puede usar python matemática.isclose():

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

avatar de usuario
volodymyr

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

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

  • Además, pytest tiene una opción para esto, no considero una buena opción agregar una dependencia adicional (en este caso, un marco de prueba completo) solo para esto.

    – Marc Tudurí

    13 de junio de 2017 a las 12:37

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad