pytest: afirma quase igual


145

Como fazer assert almost equalcom py.test para carros alegóricos sem recorrer a algo como:

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

Mais especificamente, será útil conhecer uma solução interessante para comparar rapidamente pares de flutuadores, sem desempacotá-los:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()

3
py.test agora tem um recurso que faz isso.
— dbn 21/09

Veja esta resposta para obter uma descrição desse recurso
— Tom Hale

Respostas:


232

Percebi que essa pergunta foi feita especificamente sobre py.test. O py.test 3.0 inclui uma approx()função (bem, realmente classe) que é muito útil para esse 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

A documentação está aqui: https://docs.pytest.org/en/latest/reference.html#pytest-approx


12
Agradável! Também encontrado ele funciona para seqüências de números muito por exemploassert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
— Sr. Kriss

4
@ Krr E mesmo para dictos:assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
— Antony Hatchkins

4
Isso não funciona para listas de listas: por exemplo, assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]])leva a a TypeError. Se você descobriu que o Numpy np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(veja a resposta abaixo) funcionou nesse caso.
— Kurt Peek

43

Você precisará especificar o que é "quase" para você:

assert abs(x-y) < 0.0001

para aplicar às tuplas (ou a qualquer sequência):

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
A questão pergunta como fazê-lo "sem recorrer a algo como" this
— endolith

Eu interpreto "algo assim" como uma expressão repetitiva e estranha x - d <= y <= x+d, parece que é isso que OP também significava. Se você não deseja especificar explicitamente o limite para 'quase', consulte a resposta de @ jiffyclub.
— yurib

2
py.test agora tem um recurso que faz isso. Eu adicionei uma resposta discutindo isso.
— dbn

2
@ NeilG Por que diabos isso deve ser excluído? Se houver algo obviamente errado com isso, explique o que é.
— usar o seguinte comando

1
@ user2699 A questão é como fazer isso no pytest. A maneira correta de fazer isso no pytest é usar pytest.approx. Escrever sua própria função aproximada é uma má idéia. (O de esta resposta não é ainda tão bom quanto o incluído.)
— Neil G

31

Se você tiver acesso ao NumPy, ele possui ótimas funções para comparação de ponto flutuante que já fazem comparação pareada numpy.testing.

Então você pode fazer algo como:

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

11

Algo como

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

Isso é o que o mais unido faz

Para a segunda parte

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

Provavelmente melhor envolver isso em uma função

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

Essas respostas já existem há muito tempo, mas acho que a maneira mais fácil e legível é usar o unittest, pois ele tem muitas afirmações legais, sem usá-lo para a estrutura de teste.

Obtenha asserções, ignore o resto da unittest.TestCase

(com base nesta resposta )

import unittest

assertions = unittest.TestCase('__init__')

Faça algumas afirmações

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

Implementar o teste de descompactação automática das perguntas originais

Basta usar * para descompactar seu valor de retorno sem precisar introduzir novos nomes.

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 você quer algo que funcione não apenas com flutuadores, mas, por exemplo, decimais, você pode usar o 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


Aqui, a tolerância relativa (ou diferença percentual) é conveniente para uso em alguns casos de uso, por exemplo, científico.
— karioki

3

Eu usaria nose.tools. Ele funciona bem com o corredor py.test e tem outras afirmações igualmente úteis - 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
Além do pytest ter uma opção para isso, não considero uma boa opção adicionar uma aparência extra (neste caso, toda uma estrutura de teste) apenas para isso.
— Marc Tudurí
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.