1

I am learning to use pytest. It complains about the call difference(5.2, 5.6) returning the value -0.39999999999999947 instead of 0.4. Surely these two numbers are not the same, but is there a way for pytest to tell that this error is OK?

Elaborate form of my question:
My root directory looks like this:

Learn-Python-Testing
|
|-  math_functions
|   |-  __init__.py
|
|-  tests.py

This is the implementation of my math function:

# file math_functions/__init__.py

import numpy as np

def difference(a: float, b: float) -> float:
    return a-b

And here are four test cases, of which one will fail:

# file tests.py

import numpy as np
import pytest
import math_functions   # the self-written module I want to test

@pytest.mark.parametrize("argument_1, argument_2, difference", [
    (71, 15, 56),
    (71, -15, 86),
    (5.2, 5.6, -0.4),
    (152, 156, -4),
])
def test_difference(argument_1, argument_2, difference):
    assert math_functions.difference(argument_1, argument_2) == difference

When running
~/Learn-Python-Testing$ pytest tests.py it prints the following:

=============================== test session starts ===============================
platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
Using --randomly-seed=3306462934
rootdir: /home/nerdontour/Learn-Python-Testing
plugins: randomly-3.11.0, anyio-3.3.2, dash-2.0.0
collected 4 items                                                                 

tests.py ..F.                                                               [100%]

==================================== FAILURES =====================================
__________________________ test_difference[5.2-5.6--0.4] __________________________

argument_1 = 5.2, argument_2 = 5.6, difference = -0.4

    @pytest.mark.parametrize("argument_1, argument_2, difference", [
        (71, 15, 56),
        (71, -15, 86),
        (5.2, 5.6, -0.4),
        (152, 156, -4),
    ])
    def test_difference(argument_1, argument_2, difference):
>       assert math_functions.difference(argument_1, argument_2) == difference
E       assert -0.39999999999999947 == -0.4
E        +  where -0.39999999999999947 = <function difference at 0x7fa0e41f3a60>(5.2, 5.6)
E        +    where <function difference at 0x7fa0e41f3a60> = math_functions.difference

tests.py:25: AssertionError
============================= short test summary info =============================
FAILED tests.py::test_difference[5.2-5.6--0.4] - assert -0.39999999999999947 == ...
=========================== 1 failed, 3 passed in 0.13s ===========================

Now I am aware that indeed -0.39999999999999947 != -0.4. However, you would certainly agree that I do not want my test system to hint me on such a small computation error (would you?). Is there a way to tell pytest that any result within the range [-0.39998, -0.40002] (say) is OK? Or is there a different best practice for this problem?

NerdOnTour
  • 634
  • 4
  • 15
  • You can check if the value is in a acceptable precission range instead of checking the exact number, but it depends of your program requirements. Another solution could be using Sympy to use mathematical notation if high precission is required – Fran Arenas Jan 26 '22 at 10:18
  • "but is there a way for pytest to tell that this error is OK?" That's not the point of pytest – you *don't* want it to say "this isn't what they expected but surely it's okay" just because it feels like it. If you are fine with less precision, it's on you to tell pytest. – MisterMiyagi Jan 26 '22 at 10:23
  • "you would certainly agree that I do not want my test system to hint me on such a small computation error" - absolutely not. Pytest should never make a decision like this implicitly, in case it's testing an application where such imprecision DOES matter. – Shadow Jan 26 '22 at 10:36
  • @MisterMiyagi and Shadow You are both absolutely right. What I meant to write (and apparently didn't convey) is that I would like tell pytest, in this specific case, to not raise an error. – NerdOnTour Jan 26 '22 at 10:40

1 Answers1

1

You can use math.isclose() to test for floating "equivalence" within a certain amount:

math.isclose(-0.39999999999999947, -0.4)
True

I know unittest and numpy have their own forms of this, so maybe Pytest or your specific module does as well. If not, your best bet is probably math.isclose().

user1717828
  • 7,122
  • 8
  • 34
  • 59
  • This is a nice idea, but it doesn't work well with the `parametrize`: I would ideally like to specify the allowed range for each of the test samples. – NerdOnTour Jan 26 '22 at 10:45
  • I don't understand why you say it doesn't work well with `pytest.mark.parametrize`. Change your assert to `assert math.isclose(calculated, expected)`. You can pass a `rel_tol` parameter to `isclose()` to specify the range you want. – user1717828 Jan 26 '22 at 12:43
  • Oh, of course. I can pass an argument `rel_tol` (or `abs_tol`, which I think I will use) to each test, and of course I can configure for each parametrized test case the allowed tolerance. Thank you for pointing me to this explicitly. – NerdOnTour Jan 26 '22 at 13:09