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.