【问题标题】:pytest: assert almost equalpytest:断言几乎相等
【发布时间】:2012-01-23 12:17:56
【问题描述】:

如何使用 py.test 为浮点数执行 assert almost equal 而无需求助于:

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

更具体地说,了解一个快速比较浮点对的简洁解决方案将很有用,而无需解包:

assert (1.32, 2.4) == i_return_tuple_of_two_floats()

【问题讨论】:

  • py.test 现在有一个功能可以做到这一点。
  • 查看this answer了解该功能的描述

标签: python unit-testing pytest


【解决方案1】:

我注意到这个问题专门询问了 py.test。 py.test 3.0 包含一个 approx() 函数(嗯,真的是类),它对此非常有用。

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

文档是here

【讨论】:

  • 不错!还发现它也适用于数字序列,例如assert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
  • @Mr Kriss 甚至是听写:assert {'a': 0.1+0.2} == pytest.approx({'a': 0.3})
  • 这不适用于列表列表:例如,assert [[0.1 + 0.2], [0.2 + 0.4]] == pytest.approx([[0.3], [0.6]]) 导致TypeError。如果发现 Numpy 的 np.testing.assert_allclose([[0.1 + 0.2], [0.2 + 0.4]], [[0.3], [0.6]])(请参阅下面的答案)确实适用于这种情况。
  • 值得注意的是,第二个位置参数是相对容差,但您也可以指定绝对容差:0.2 == pytest.approx(0.3, 0.1) # returns false; 0.2 == pytest.approx(0.3, abs=0.1) # returns true
【解决方案2】:

你必须为你指定什么是“几乎”:

assert abs(x-y) < 0.0001

应用于元组(或任何序列):

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

【讨论】:

  • 问题询问如何“不诉诸于”这样的事情
  • 我将“类似这样的东西”解释为像x - d &lt;= y &lt;= x+d 这样的重复而尴尬的表达,似乎这也是OP 的意思。如果您不想明确指定“几乎”的阈值,请参阅@jiffyclub 的回答。
  • py.test 现在具有执行此操作的功能。我添加了一个讨论它的答案。
  • @NeilG 为什么要删除这个?如果它有明显的问题,请解释它是什么。
  • @user2699 问题是如何在 pytest 中做到这一点。在 pytest 中正确的做法是使用pytest.approx。编写自己的近似函数是个坏主意。 (这个答案中的那个甚至不如包含的那个。)
【解决方案3】:

如果您可以访问 NumPy,它具有出色的浮点比较功能,已经与 numpy.testing 进行了成对比较。

然后你可以这样做:

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

【讨论】:

    【解决方案4】:

    这些答案已经存在了很长时间,但我认为最简单也是最易读的方法是使用 unittest 的 many nice assertions 而不将其用于测试结构。

    获取断言,忽略 unittest.TestCase 的其余部分

    (基于this answer

    import unittest
    
    assertions = unittest.TestCase('__init__')
    

    做出一些断言

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

    实现原题的自动解包测试

    只需使用 * 解压返回值,无需引入新名称。

    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
    

    【讨论】:

      【解决方案5】:

      类似

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

      unittest 就是这样做的

      第二部分

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

      最好将其包装在一个函数中

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

      【讨论】:

        【解决方案6】:

        如果您想要的东西不仅适用于浮点数,还适用于小数,您可以使用 python 的 math.isclose():

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

        【讨论】:

        • 这里的相对公差(或百分比差异)在某些用例中使用起来很方便,例如科学。
        【解决方案7】:

        我会使用nose.tools。它与 py.test 运行器配合得很好,并且还有其他同样有用的断言 - assert_dict_equal()、assert_list_equal() 等。

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

        【讨论】:

        • 除了 pytest 有一个选项,我不认为一个好的选项只为此添加额外的依赖(在这种情况下,整个测试框架)。
        【解决方案8】:

        可以用round()

        a, b = i_return_tuple_of_two_floats()
        assert (1.32, 2.4) == round(a,2), round(b,1)
        

        【讨论】:

          猜你喜欢
          • 2014-06-26
          • 1970-01-01
          • 2020-11-27
          • 2018-04-05
          • 1970-01-01
          • 1970-01-01
          • 2021-12-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多