由于四舍五入误差导致的Python单元测试失败

5
我构建了一个几何变换的类。当我运行单元测试时,由于方法内部的操作产生了舍入误差,导致测试失败。
在我的测试中,我比较了其中一个方法的结果,该方法应返回点(2,2,0),但由于舍入误差,它返回了(1.9999999999999996, 1.9999999999999996, 0.0)。
Finding files... done.
Importing test modules ... done.

** DEBUG_45 from the method point=(1.9999999999999996, 1.9999999999999996, 0.0)
======================================================================
FAIL: testPointCoord (vectOper.test.TestNearestPoint.TestNearestPoint)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\src\vectOper\test\TestNearestPoint.py", line 14, in testPointCoord
self.assertEqual(pointCoord, (2,2,0), "nearest point failed")
AssertionError: nearest point failed

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

从计算的角度来看是可以接受的,但我不希望我的代码在简单的单元测试中失败。

import unittest
from vectOper.nearestPoint import NearestPoint

class TestNearestPoint(unittest.TestCase):

    def testPointCoord(self):
        nearestPoint = NearestPoint()
        pointCoord = nearestPoint.pointCoord(samplePoint=(2,2,2),lineStart=(0,0,0), lineVect=(1,1,0))
        self.assertEqual(pointCoord, (2,2,0), "nearest point failed")

如何正确解决这样的问题?显然我不能将输出数字四舍五入或转换为整数,因为通常情况下不是这种情况。 是否有一种编写单元测试来忽略舍入误差的方法? 还有其他解决问题的方法吗?

编辑: 正如another的答案所建议的那样,可以使用self.assertAlmostEqual来解决这个问题,但问题在于我需要测试一个元组的输入。在所有建议之后,我试图做到:

def testPointCoord(self):
    nearestPoint = NearestPoint()
    pointCoord = nearestPoint.pointCoord(samplePoint=(2,2,2),lineStart=(0,0,0), lineVect=(1,1,0))
    self.assertAlmostEqual(pointCoord[0], 2, places=7, msg="nearest point x-coodr failed")
    self.assertAlmostEqual(pointCoord[1], 2, places=7, msg="nearest point y-coodr failed")
    self.assertAlmostEqual(pointCoord[2], 0, places=7, msg="nearest point z-coodr failed")

但我需要以某种方式自动化它,因为后来我需要测试一系列元组作为向量场的样本点坐标。作为重复的解决方案建议的解决方案只是半成品,因为如果列表中有100个元组,则编写300个以上的比较会有些繁琐。

3
可能是Unittest (sometimes) fails because floating-point imprecision的重复问题。 - Martijn Pieters
1
(注意,assertAlmostEqual没有比较元组的魔法,因此您需要单独测试每个三维坐标) - Wooble
据我所知,使用“可能重复”的建议中提到的assertRaises将为可接受的数字(如我展示的情况)和远离真相的不可接受的数字引发异常。我认为这并不能解决问题。 - tomasz74
@tomasz74:我在链接的重复内容中没有看到assertRaises的任何用途。 - Wooble
@Wooble 谢谢,我按照顶部的链接,进入了另一个答案,但现在它指向相同的内容,抱歉。我尝试弄清楚如何使用assertAlmostEqual来比较元组中的元素。 - tomasz74
1个回答

4
为什么不使用map函数在每个维度上都使用assertAlmostEqual?我无法访问您的类,因此我在此处编写了一个类似的示例:
from unittest import TestCase

class Test_Tuple_Equality(TestCase):
    def test_tuple_equality_True(self):
        p1 = (0.00000001, 0.00000000001, 0)
        p2 = (0,0,0)
        map(lambda x, y: self.assertAlmostEqual(x,y), p1, p2)

    def test_tuple_equality_False(self):
        p1 = (0.00000001, 0.00000000001, 0)
        p2 = (1,0,0)
        map(lambda x, y: self.assertAlmostEqual(x,y), p1, p2)

Map会将一个n维元组比较转换为n个浮点数的比较。

你甚至可以创建一个compare_points函数,像这样:

def compare_points(self, p1, p2):
    map(lambda x,y: self.assertAlmostEqual(x,y), p1,p2)

然后在您的测试中使用它。

另一个解决方案是使用 numpy 的方法:

import numpy

>>>numpy.testing.assert_almost_equal((2,2,0), (1.9999999999,2,0), decimal=7, err_msg='', verbose=True)

Numpy的安装有些麻烦,但如果你已经在使用它,那么它将是最合适的选择。


1
我尝试实现上述内容,但代码出现错误,你知道错误来自哪里吗?map(lambda x, y: self.assertAlmostEqual(x,y, "nearest point failed"), p1, p2)
File "C:\Python27\lib\unittest\case.py", line 552, in assertAlmostEqual if round(abs(second-first), places) == 0: TypeError: 'str' object cannot be interpreted as an index
- tomasz74
这在我的电脑上可以运行。你确定p1和p2是元组吗? 尝试通过迭代它们,如p1 [0],p2 [1],... - Lucas Ribeiro
我尝试了,我也使用了您的代码,但是我遇到了这个错误。numpy.testing.assert_almost_equal() 完美地工作。我在 Windows 7 上使用 Python 2.7。 - tomasz74
谢谢,我应该能够解决它。很奇怪,但如果将消息作为关键参数map(lambda x, y: self.assertAlmostEqual(x,y, msg='msg'), p1, p2)接受它。 - tomasz74

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接