-1

Here are two variables: earnings_forecast, actual_earning (numerical variables)

I want to assert if both these variables are equal with a difference of ±2% acceptable with respect to actual_earning variable.

Suppose: earnings_forecast = 6 actual_earnings = 5.19

I cannot use assertEqual(earnings_forecast, actual_earnings) because it will try do an exact match, instead I want to assert both these variables are almost equal with ±2% difference acceptable.

Avanti
  • 323
  • 5
  • 14
  • 6
    Two percent of which? Anyway just calculate the difference and assert its absolute value is smaller than the number you want. – RemcoGerlich Jan 23 '17 at 14:10
  • Are these numerical values, or just any objects? – user2390182 Jan 23 '17 at 14:13
  • Implement your own `compare` function that will return `True` or `False` and use the `unittest` built-in `assertTrue` (given that the built-in `assertAlmostEqual` won't be of help in this case). – DeepSpace Jan 23 '17 at 14:14
  • Possible duplicate of [Is there a way to use Python unit test assertions outside of a TestCase?](http://stackoverflow.com/questions/18084476/is-there-a-way-to-use-python-unit-test-assertions-outside-of-a-testcase) – Abhijeet Jan 30 '17 at 04:10
  • Assert two values `tc = unittest.TestCase('__init__') tc.assertEqual(a,b)` [Reference](http://stackoverflow.com/a/18084492/452708) – Abhijeet Jan 30 '17 at 04:11

5 Answers5

20

You can use the new isclose function introduced in Python 3.5

PEP 485 adds the math.isclose() and cmath.isclose() functions which tell whether two values are approximately equal or “close” to each other. Whether or not two values are considered close is determined according to given absolute and relative tolerances. Relative tolerance is the maximum allowed difference between isclose arguments, relative to the larger absolute value

import math
a = 100.0
b = 102.0
c = 103.0

assert math.isclose(a,b, rel_tol=0.02)
assert math.isclose(a,c, rel_tol=0.02)
Jul3k
  • 1,046
  • 1
  • 10
  • 19
  • Oh that's posh. Scrap my way. – Bathsheba Jan 23 '17 at 14:16
  • equivalent to `abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )` – Jose Ricardo Bustos M. Jan 23 '17 at 14:17
  • 1
    Numpy also has a similar implementation of this: [`numpy.isclose`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.isclose.html) and an array-based equivalent [`numpy.allclose`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html#numpy.allclose) which can increase performance for large datasets. – Ivo Merchiers Jun 28 '19 at 11:24
1
abs(earnings_forecast - actual_earning) < 0.01 * abs(earnings_forecast + actual_earning)

is a nice way of doing it, which gives you a good symmetric 2% difference on either side. It also doesn't suffer from pitfalls that can arise of one of the values is zero.

There are other definitions, but like the one above, they have their own pros and cons.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Bathsheba
  • 231,907
  • 34
  • 361
  • 483
1

Simply define a new test:

def assertNearlyEqual(self,a,b,fraction=0.02,msg=None):
    if abs(a-b) > abs(fraction*a):
        if msg is None:
            self.fail("The given numbers %s and %s are not near each other."%(a,b))
        else:
            fail(msg)

and call it with your two variables:

self.assertNearlyEqual(earnings_forecast,actual_earning)
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
1

Simple approach:

a, b = sorted(map(float, (a, b)))
assert a + abs(a)*1.02 >= b

You can use a custom Testcase subclass for use in tests:

class FooTestCase(TestCase):
    def assertAlmostEqual(a, b):
        a, b = sorted(map(float, (a, b)))
        self.assertTrue(a + abs(a)*1.02 >= b)
user2390182
  • 72,016
  • 6
  • 67
  • 89
1

For those who still use Python 2.x, you could also use numpy.isclose()

from numpy import isclose as isclose
a = 100.0
b = 100.01

print isclose(a,b, atol=0.02)  # True

From the documentation:

For finite values, isclose uses the following equation to test whether two floating point values are equivalent.

absolute(a - b) <= (atol + rtol * absolute(b))
crypdick
  • 16,152
  • 7
  • 51
  • 74