Precision matters, but so does practicality. This is where the pytest approx module comes into play.

The Issue With Floating Point Arithmetic

Floating-point numbers are encoded in computer hardware using base 2 (binary) fractions.

You know that the decimal fraction 0.125 can be written as 1/10 + 2/100 + 5/1000. Similarly, the binary fraction 0.001 corresponds to 0/2 + 0/4 + 1/8.

Take the example of the fraction 1/3. It can be approximated in base 10 as follows: 0.3, or 0.33 or even better 0.333 and so on.

Understanding Tolerance Levels

Understanding tolerance levels is crucial when dealing with numerical approximations in testing.

Tolerance determines the acceptable deviation between two numbers for them to be considered practically equal.

Syntax of Pytest Approx

The syntax for using the pytest approx functionality in Pytest is quite straightforward. You can use it to perform approximate comparisons between numerical values, accounting for the inherent imprecision of floating-point calculations.

Here’s the basic syntax:

assert actual_value == pytest.approx(expected_value, rel=None, abs=None, nan_ok=False)

Here’s a simple example to implement pytest approx:
tests/unit/test_pytest_approx_1.py

import pytest  
  
def divide(a, b):  
return a / b  
  
def test_exact_comparison():  
result = divide(1, 3)  
assert result == 0.3333333333333333 # Exact value  
  
def test_approximate_comparison():  
result = divide(1, 3)  
assert result == pytest.approx(0.3333333333333333) # Approximate value  
  
@pytest.mark.xfail(reason="This test is currently expected to fail")  
def test_approximation_failure():  
result = divide(1, 3)  
assert result == pytest.approx(0.333) # This test will fail due to approximation  
  1. test_exact_comparison: Comparing the result of the divide(1, 3) function call, which computes 1 / 3, with the exact value 0.3333333333333333. Since the comparison is exact, the test passes.
  2. test_approximate_comparison: This test also compares the result of divide(1, 3) with the value 0.3333333333333333, but this time using pytest.approx. This test passes because pytest.approx considers the small variations due to floating-point imprecision.
  3. test_approximation_failure: Here we compare the result of divide(1, 3) with the value 0.333 using pytest.approx.

Approximating Lists And Arrays

When comparing a list of calculated values with expected results, Pytest Approx simplifies the process, allowing you to assert approximate equality without manually addressing each element’s precision.

Similarly, when you are comparing arrays you can employ pytest.approx to verify their equality within specified tolerances.

Approximating Dictionaries

For dictionaries, Pytest Approx handles key-value pairs with precision.
tests/unit/test_pytest_approx_6.py

import pytest
 
def test_measurements():
    expected = {
        'length': 10.0,
        'temperature': 25.5,
        'pressure': 1013.25,
    }
    
    calculated = {
        'length': 10.0001,        # Slightly different due to calculations
        'temperature': 25.4999,   # Slightly different due to calculations
        'pressure': 1013.24,      # Slightly different due to calculations
    }
    
    tolerance = {
        'length': 0.01,           # Tolerance for length values
        'temperature': 0.05,      # Tolerance for temperature values
        'pressure': 0.1,          # Tolerance for pressure values
    }
    
    for key in expected:
        assert calculated[key] == pytest.approx(expected[key], abs=tolerance[key])

In scenarios where you’re comparing calculated dictionary values with expected outcomes, Pytest Approx lets you assert approximate equality while accounting for floating-point discrepancies.