pytest: nyatakan hampir sama

145

Bagaimana hubungannya assert almost equaldengan py.test untuk mengapung tanpa menggunakan sesuatu seperti:

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

Lebih khusus akan berguna untuk mengetahui solusi yang rapi untuk dengan cepat membandingkan pasangan float, tanpa membongkar mereka:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()
Vladimir Keleshev
sumber
3
py.test sekarang memiliki fitur yang melakukan ini.
dbn
Lihat jawaban ini untuk deskripsi fitur itu
Tom Hale

Jawaban:

232

Saya perhatikan bahwa pertanyaan ini secara khusus bertanya tentang py.test. py.test 3.0 termasuk approx()fungsi (well, really class) yang sangat berguna untuk tujuan ini.

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

Dokumentasi ada di sini: https://docs.pytest.org/en/latest/reference.html#pytest-approx

dbn
sumber
12
Bagus! Juga menemukan itu berfungsi untuk urutan angka juga misalnyaassert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
Mr Kriss
4
@Mr Kriss Dan bahkan untuk dikte:assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
Antony Hatchkins
4
Ini tidak berfungsi untuk daftar daftar: misalnya, assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]])mengarah ke a TypeError. Jika ternyata Numpy np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(lihat jawaban di bawah) berhasil untuk kasus ini.
Kurt Peek
43

Anda harus menentukan apa yang "hampir" untuk Anda:

assert abs(x-y) < 0.0001

untuk diterapkan pada tupel (atau urutan apa pun):

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
sumber
3
Pertanyaannya menanyakan bagaimana cara melakukannya "tanpa menggunakan sesuatu seperti" ini
endolith
Saya menafsirkan "sesuatu seperti ini" sebagai ekspresi berulang dan canggung seperti x - d <= y <= x+d, sepertinya itulah yang dimaksud OP juga. Jika Anda tidak ingin menentukan ambang secara eksplisit untuk 'hampir', lihat jawaban @ jiffyclub.
yurib
2
py.test sekarang memiliki fitur yang melakukan ini. Saya telah menambahkan jawaban untuk membahasnya.
dbn
2
@ NeilG Kenapa ini harus dihapus? Jika ada sesuatu yang salah dengan itu, tolong jelaskan apa itu.
user2699
1
@ user2699 Pertanyaannya adalah bagaimana melakukan ini di pytest. Cara yang benar untuk melakukannya di pytest adalah menggunakan pytest.approx. Menulis fungsi perkiraan Anda sendiri adalah ide yang buruk. (Jawaban dalam jawaban ini bahkan tidak sebaik jawaban yang disertakan.)
Neil G
31

Jika Anda memiliki akses ke NumPy, ia memiliki fungsi hebat untuk perbandingan titik mengambang yang sudah melakukan perbandingan berpasangan numpy.testing.

Maka Anda dapat melakukan sesuatu seperti:

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

Sesuatu seperti

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

Itulah yang unittest tidak

Untuk bagian kedua

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

Mungkin lebih baik untuk membungkusnya dalam suatu fungsi

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

Jawaban ini sudah ada sejak lama, tetapi saya pikir cara termudah dan juga paling mudah dibaca adalah menggunakan unittest untuk banyak asersi bagus tanpa menggunakannya untuk struktur pengujian.

Dapatkan pernyataan, abaikan sisa unittest.TestCase

(berdasarkan jawaban ini )

import unittest

assertions = unittest.TestCase('__init__')

Buat beberapa pernyataan

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

Laksanakan tes pembongkaran otomatis pertanyaan asli

Cukup gunakan * untuk membongkar nilai pengembalian Anda tanpa harus memasukkan nama baru.

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

Jika Anda menginginkan sesuatu yang berfungsi tidak hanya dengan float tetapi misalnya Desimal Anda dapat menggunakan python math.isclose:

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

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

validname
sumber
Di sini toleransi relatif (atau perbedaan persentase) nyaman digunakan dalam beberapa kasus penggunaan, misalnya ilmiah.
Karioki
3

Saya akan menggunakan nose.tools. Ini dimainkan dengan baik oleh pelari py.test dan memiliki pernyataan yang sama bermanfaat lainnya - assert_dict_equal (), assert_list_equal (), dll.

from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7 
volodymyr
sumber
2
Selain pytest memiliki opsi untuk ini, saya tidak menganggap opsi yang baik menambahkan kemandirian ekstra (dalam hal ini, seluruh kerangka pengujian) hanya untuk ini.
Marc Tudurí