Skip to content

Instantly share code, notes, and snippets.

@dishbreak
Created May 12, 2019 17:34
Show Gist options
  • Save dishbreak/0f65fa775da493b20af9aee89e2ae45f to your computer and use it in GitHub Desktop.
Save dishbreak/0f65fa775da493b20af9aee89e2ae45f to your computer and use it in GitHub Desktop.
Assert a Partial Match on a MagicMock Object
import os
import mock
class AnyStringEndingWith(object):
def __init__(self, suffix):
self.suffix = suffix
def __repr__(self):
return "<a string ending with '%s'>" % self.suffix
def __eq__(self, other):
return other.endswith(self.suffix)
@mock.patch("os.path")
def test_matcher(mock_path):
mock_path.isfile(os.path.join("not", "a", "real", "path"))
mock_path.isfile.assert_called_with(AnyStringEndingWith("path"))
@Sharadh
Copy link

Sharadh commented May 13, 2019

Nice! If anyone’s curious why that works, it’s because assert_called_with (and family of such) converts the args, kwargs of the call into a tuple, then does:

        expected = self._call_matcher((args, kwargs))
        actual = self._call_matcher(self.call_args)
        if expected != actual:
            raise ...

From https://github.com/testing-cabal/mock/blob/e0180b98d0e07e895a3f699b7e9afcac4716fc03/mock/mock.py#L940-L944

Tuple equality == equality of each ordered element in the tuple. So, by overriding the __eq__ of any object that is passed in, one can control the conditions of match.

For another example, check out what the sentinel object mock.ANY does:

class _ANY(object):
    "A helper object that compares equal to everything."

    def __eq__(self, other):
        return True

    def __ne__(self, other):
        return False

    def __repr__(self):
        return '<ANY>'

ANY = _ANY()

From: https://github.com/testing-cabal/mock/blob/e0180b98d0e07e895a3f699b7e9afcac4716fc03/mock/mock.py#L2078-L2092

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment