classification
Title: More matchers in unittest.mock
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: FR4NKESTI3N, Petter S, lisroach, xtreak
Priority: normal Keywords:

Created on 2019-01-04 11:04 by Petter S, last changed 2019-01-25 23:30 by FR4NKESTI3N.

Messages (8)
msg332968 - (view) Author: Petter S (Petter S) * Date: 2019-01-04 11:04
The ``ANY`` object in ``unittest.mock`` is also pretty useful when verifying dicts in tests:

    self.assertEqual(result, {
        "message": "Hi!",
        "code": 0,
        "id": mock.ANY
    })

Then it does not matter what the (presumably randomly generated) id is. For the same use cases, objects like ``APPROXIMATE`` (for approximate floating-point matching) and ``MATCHES`` (taking a boolean lambda) would be pretty useful, I think.
msg333210 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2019-01-08 05:42
Looking at the code ANY is simply implemented with __eq__ always returning True at https://github.com/python/cpython/blob/e61cc481e02b758c8d8289163102c236d0658a55/Lib/unittest/mock.py#L1957 . I am not sure how APPROXIMATE can be implemented in terms of floating point like rounding up or down? Do you have any examples in mind over a sample implementation or example of how they should behave in various scenarios?
msg333222 - (view) Author: Petter S (Petter S) * Date: 2019-01-08 11:07
Yes, something like this:

    class APPROXIMATE:
        """Takes a floating point number and implements approximate equality."""

        def __init__(self, value):
            self.value = value

        def __eq__(self, other):
            return abs(self.value - other) / (abs(self.value) + abs(other)) < 1e-6

        def __repr__(self):
            return f"APPROXIMATE({self.value})"


Then the following would hold:

    got = {
    "name": "Petter",
    "length": 1.900001
    }

    expected = {
    "name": "Petter",
    "length": APPROXIMATE(1.9)
    }

    assert got == expected

But not

    got["length"] = 1.8
    assert got == expected
msg333223 - (view) Author: Yash Aggarwal (FR4NKESTI3N) * Date: 2019-01-08 11:29
I feel it would be better to have tolerance as an argument.
msg333330 - (view) Author: Petter S (Petter S) * Date: 2019-01-09 16:05
Agreed! The code above was a quick example. There are also functions in the standard library for approximate float matching that the "real" code would use.
msg333420 - (view) Author: Lisa Roach (lisroach) * (Python committer) Date: 2019-01-10 21:42
APPROXIMATE feels like it might lead to code smell to me, if I know roughly what the float should be why would I not want to test it for exactness? It could end up hiding inconsistencies the tests should be catching.
msg333443 - (view) Author: Petter S (Petter S) * Date: 2019-01-11 08:32
I am of the opposite opinion. :-) 

> if I know roughly what the float should be why would I not want to test it for exactness?

When testing algorithms, it is often the case that the answer should be mathematically exactly 2, but due to floating-point inexactness it becomes, say, 1.9999999997 in practice. If I then test for exactly 1.9999999997 the test becomes very brittle and sensitive for e.g. order of multiplications.

Testing floating point numbers with a relative error is essential in many application.
msg334379 - (view) Author: Yash Aggarwal (FR4NKESTI3N) * Date: 2019-01-25 23:30
> due to floating-point inexactness
+1 Its not easy to predict when calculated value will not be equal to expected theoretical value. for example math.cos(radians(90)) is something like 6e-17 rather than 0.
Testing for this exact value would be just awkward.
assertAlmostEqual() is already there in unittest for such comparisons so it wouldn't be completely nonsensical to have something like APPROX
History
Date User Action Args
2019-01-25 23:30:19FR4NKESTI3Nsetmessages: + msg334379
2019-01-11 08:32:44Petter Ssetmessages: + msg333443
2019-01-10 21:42:05lisroachsetnosy: + lisroach
messages: + msg333420
2019-01-09 16:05:39Petter Ssetmessages: + msg333330
2019-01-08 11:29:52FR4NKESTI3Nsetnosy: + FR4NKESTI3N
messages: + msg333223
2019-01-08 11:07:48Petter Ssetmessages: + msg333222
2019-01-08 05:42:51xtreaksetnosy: + xtreak

messages: + msg333210
versions: + Python 3.8
2019-01-04 11:04:51Petter Screate