0

Assume I have a function defined in a module:

module_a.py

def foo(): return 10 

And I want to create an API to patch the function:

patcher.py

import mock class Patcher(object): def __enter__(self): self.patcher = mock.patch('module_a.foo', mock.Mock(return_value=15)) self.patcher.start() def __exit__(self, *args): self.patcher.stop() 

The thing is, I don't know what is the name of the module that will use my API. so a test looking like this:

test1.py

from patcher import Patcher import module_a with Patcher(): assert module_a.foo() == 15 

will work. But a test written like this:

test2.py

from patcher import Patcher from module_a import foo with Patcher(): assert foo() == 15 

will Fail.

Is there anyway not making the API user to write it's tests and modules(!) like the first option?

4
  • You don't patch functions where they're defined, you patch them where they're used. Read the mock documentation. Commented Sep 9, 2017 at 22:39
  • The developer writing tests for that 'unknown module' will know how to apply a mock patch. Writing tests for those modules is not your job. Commented Sep 9, 2017 at 22:42
  • Why are you writing the patcher in the first place? Commented Sep 9, 2017 at 22:43
  • The patcher is the API of my library. The library I am patching is more complicated then this example. Commented Sep 9, 2017 at 23:07

1 Answer 1

1

There is a way to "patch" over a function without knowing where the patch is occurring. That was the requirement for my question since the patcher is my library API, and I don't want to be given a path to each test module using my library.

The solution I found was to pass on all loaded modules and try to find foo in them, and then changing it - sorta implementing patch by myself. If the import will happen only after the Patcher is started, I loaded the module myself, and changed it too.

Now the code will look like this:

Patcher

import sys import mock from module_a import foo as _orig_foo import module_a class Patcher(object): def __init__(self): self.undo_set = set() self.fake_foo = mock.Mock(return_value=15) def __enter__(self): modules = [ module for mod_name, module in sys.modules.items() if mod_name is not None and module is not None and hasattr(module, '__name__') and module.__name__ not in ('module_a', 'patcher') ] for module in modules: for attr in dir(module): try: attribute_value = getattr(module, attr) except (ValueError, AttributeError, ImportError): # For some libraries, this happen. continue if id(attribute_value) == id(_orig_foo): setattr(module, attr, self.fake_foo) self.undo_set.add((module, attr, attribute_value)) # Solve for future imports module_a.foo = self.fake_foo def __exit__(self, *args): module_a.foo = _orig_foo for mod, attr, val in self.undo_set: setattr(mod, attr, val) self.undo_set = set() 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.