5

I build the class for geometric transformation. When I run a Unit test it fails because of rounding errors coming from the operations inside my methods.

In my test I compare the result from one of the method which should return the point (2,2,0), but because of rounding errors it returns (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)

From the calculation point of view it is acceptable, but I don't want my code to fail on the simple unit test.

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

What is a correct way to resolve problem like that? Obviously I cannot round up the output numbers or convert them to integers as normally it is not the case. Is there a way to code unit test to ignore rounding error? Is there any other way to resolve problem?

Edit: The question can be solved by using self.assertAlmostEqual as rightly suggested in another answer but the problem is that I need to test entrance of a tuple. After all suggestions I try to do:

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

but I need to automatise it somehow as later I need to test a list of tuples as the sample points' coordinates for a vector field.

The solution suggested as a duplicate is only a half measure as it would be a bit tedious write 300 more comparisons if there is 100 tuples in the list.

Community
  • 1
  • 1
tomasz74
  • 16,031
  • 10
  • 37
  • 51
  • 3
    possible duplicate of [Unittest (sometimes) fails because floating-point imprecision](http://stackoverflow.com/questions/8929005/unittest-sometimes-fails-because-floating-point-imprecision) – Martijn Pieters Sep 13 '13 at 17:24
  • 1
    (Note that `assertAlmostEqual` doesn't have any magic to compare tuples so you need to test each of the 3 coordinates separately) – Wooble Sep 13 '13 at 17:33
  • As far as I understand using assertRaises as suggested in "possible duplicate" will raise and exceptions for acceptable numbers like in case I showed and for unacceptable numbers, far away from truth. I don't think it would solve the proble – tomasz74 Sep 13 '13 at 17:34
  • @tomasz74: I don't see any use of `assertRaises` in the linked duplicate. – Wooble Sep 13 '13 at 17:39
  • @Wooble thanks, I followed a link on top, it get me to another answer but now it point to the same, sorry for that. I try to figure out `assertAlmostEqual` with application to elements of tuple. – tomasz74 Sep 13 '13 at 17:46

1 Answers1

4

Why don't you use assertAlmostEqual in each dimension using map? I don`t have access to your class, so i wrote a similar example here:

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 will transform your a n-dimension tuple comparisson into n floats comparissons.

You can even create a compare_points function, like:

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

And then use it in your tests

Another solution is to use numpy`s method for that:

import numpy

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

Numpy is a pain to install, but, if you already use it, it would be the best fit.

Lucas Ribeiro
  • 6,132
  • 2
  • 25
  • 28
  • 1
    I try to implement the above, but the code gives and error, do you know where the error coming from? `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 Sep 15 '13 at 11:35
  • It works for me here. Are you sure that p1 and p2 are tuples? Try to iterate through them, as p1[0], p2[1], ... – Lucas Ribeiro Sep 15 '13 at 13:16
  • I tried, I used also exactly your code, and I have this error. The `numpy.testing.assert_almost_equal()` works perfectly. I use python 2.7 on windows 7 – tomasz74 Sep 15 '13 at 13:19
  • Thanks, I should figure it out. It is strange, but it accept the message if it is as a key arg `map(lambda x, y: self.assertAlmostEqual(x,y, msg='msg'), p1, p2)` – tomasz74 Sep 16 '13 at 09:06