Patching a method from other file is not working - python

I have a method say _select_warehouse_for_order in api/controllers/orders.py file. The method is not part of any class.
Now, I have a new file say api/controllers/dispatchers.py where i need to know which warehouse was selected. I am calling _select_warehouse_for_order from this file to get this information.
Now, in my test cases, I am patching _select_warehouse_for_order like this
from unittest.mock import patch, call
def test_delivery_assignment(self, app_context):
with patch('api.controllers.orders._select_warehouse_for_order') as mock_selected_wh:
mock_selected_wh.return_value = {}
app_context.client.set_url_prefix('/v2')
response = app_context.client.get('/delivery/dispatch')
assert response.status_code == 200
The problem that i am facing is that my patch is not returning empty dictionary. when i started debugging, i noticed that its executed the actual code in _select_warehouse_for_order. Am i missing something here?
Update:
Here is the code in dispatchers.py
from api.controllers.orders import _select_warehouse_for_order
#bp.route("/dispatch")
#login_required
def dispatch():
warehouses = _select_warehouse_for_order(request=request)
if len(warehouses) == 0:
logger.info("No warehouse selected")
return
logger.info("Selected warehouse: %s", warehouses[0].name)
# return response

You must patch where the method is used, not where it is declared. In your case, you are patching 'api.controllers.orders._select_warehouse_for_order' which is where the method is declared. Instead, patch 'dispatchers._select_warehouse_for_order' (possibly prefixed with whatever package contains dispatchers).
The reason for this is because when you do
from api.controllers.orders import _select_warehouse_for_order
you declare a name _select_warehouse_for_order in dispatchers.py that refers to the function which is declared in api/controllers/orders.py. Essentially you have created a second reference to the function. Now when you call
warehouses = _select_warehouse_for_order(request=request)
you are using the reference in dispatchers.py, not the one in api/controllers/orders.py. So in order to replace this function with a patch, you have to use dispatchers._select_warehouse_for_order.
Notice how import is different in python than in Java because we create a new name and assign it to an existing function or class. On the other hand, Java imports tell the compiler where to look for a class when it is mentioned in the code.

Related

Python: How to use mock or patch to check that a method inside another method has run?

I've read through a whole bunch of answers, but still don't understand where I need to mock and/or which class/module to patch and keep getting module does not have the attribute errors.
I have a View class which contains a method I'd like to test. That method does not return anything. What it does is: checks an if/else statement based on the method's parameter, and in both cases fires off another method (imported from another module) with different args. I want to see if that inner method is being called with the correct arguments.
Here's the simplified code:
from django.views import View
from django.conf import settings
from my_project import email
class ProcessSub(View):
def handle_ineligible(ineligible_dict):
if ineligible_dict["status"] == "NOT_FOUND":
email.send_email(settings.NOT_FOUND_EMAIL_ID)
else:
email.send_email(settings.INELIGIBLE_EMAIL_ID)
What I want to do in my test is call handle_ineligible with a known status and check whether email.send_email was called with the right EMAIL_ID.
What I've tried:
#mock.patch('myproject.services.email.send_email')
def test_not_found(self, mock_email_send):
INELIGIBLE_DICT = {'status': 'NOT_FOUND'}
view = ProcessSub()
view.handle_ineligible(INELIGIBLE_DICT)
mock_email_send.assert_called_with(settings.INELIGIBLE_EMAIL_ID)
This fails with the following AssertionError:
E AssertionError: expected call not found.
E Expected: send_email('asdf')
E Actual: not called.

Monkey patching values of a function

I have a small function as follows:
def write_snapshot_backup_monitoring_values():
try:
snapshot_backup_result = 'my result'
with open(config.MONITOR_SNAPSHOT_BACKUP_FILE, "w") as snapshot_backup_file:
snapshot_backup_file.write(snapshot_backup_result)
except Exception as exception:
LOG.exception(exception)
where config.MONITOR_SNAPSHOT_BACKUP_FILE is declared in a config file with value = /home/result.log
when I try to write a test case using pytest and I call this function as follows:
constants.MONITOR_SNAPSHOT_BACKUP_FILE = "/tmp/result.log"
#pytest.mark.functional_test
def test_write_snapshot_backup_monitoring_values():
utils.write_snapshot_backup_monitoring_values()...
I want to monkey patch the value for config.MONITOR_SNAPSHOT_BACKUP_FILE with constants.MONITOR_SNAPSHOT_BACKUP_FILE which I have declared in the test case file. Basically I want that while runnning the test case it should create /tmp/result.log and not /home/result.log How can I do that? I am new to monkey patching in python.
You don't clear up what config is, so I assume it is another module you have imported. There's no specific technique for monkey-patching, you just assign the value. It's just a name for adding/modifying attributes at runtime.
config.MONITOR_SNAPSHOT_BACKUP_FILE = constants.MONITOR_SNAPSHOT_BACKUP_FILE
However, there's one thing to keep in mind here: Python caches imported modules. If you change this value, it will change for other python modules that have imported config and run in the same runtime. So, be careful that you don't cause any side effects.

is it possible to revert (return to old method) python monkey patch

I use a specialized python module which modifies some of the Django class methods in the runtime (aka monkey-patching). If I need these 'old' versions is it possible to 'come back' to them overriding monkey patching?
Something like importing the initial version of these classes, for example?
Here is an example of how patching was done in the package:
from django.template.base import FilterExpression
def patch_filter_expression():
original_resolve = FilterExpression.resolve
def resolve(self, context, ignore_failures=False):
return original_resolve(self, context, ignore_failures=False)
FilterExpression.resolve = resolve
It depends on what the patch did. Monkeypatching is nothing special, it's just an assignment of a different object to a name. If nothing else references the old value anymore, then it's gone from Python's memory.
But if the code that patched the name has kept a reference to the original object in the form of a different variable, then the original object is still there to be 'restored':
import target.module
_original_function = target.module.target_function
def new_function(*args, **kwargs):
result = _original_function(*args, **kwargs)
return result * 5
target.module.target_function = new_function
Here the name target_function in the target.module module namespace was re-bound to point to new_function, but the original object is still available as _original_function in the namespace of the patching code.
If this is done in a function, then the original could be available as a closure too. For your specific example, you can get the original with:
FilterExpression.resolve.__closure__[0].cell_contents
or, if you prefer access by name:
def closure_mapping(func):
closures, names = func.__closure__, func.__code__.co_freevars
return {n: c.cell_contents for n, c in zip(names, closures)}
original_resolve = closure_mapping(FilterExpression.resolve)['original_resolve']
Otherwise, you can tell Python to reload the original module with importlib.reload():
import target.module
importlib.reload(target.module)
This refreshes the module namespace, 'resetting' all global names to what they'd been set to at import time (any additional names are retained).
Note, however, that any code holding a direct reference to the patched object (such as your class object), would not see the updated objects! That's because from target.module import target_function creates a new reference to the target_function object in the current namespace and no amount of reloading of the original target.module module will update any of the other direct references. You'd have to update those other references manually, or reload their namespaces too.

Unit Test Behavior with Patch (Flask)

I am trying to patch methods in my flask api but it appears that the method call is not being replaced. Does app.test_client() do something under the hood that I am missing.
For example if I run
#patch('k.stats.mstats')
def test_ps(self, mstats):
mstats.return_value = (1, 2, 3)
rv = self.app.get('/ps/')
and I run through the debugger to the point below:
#app.route('/ps/', methods=['GET'])
def ps():
import pdb
pdb.set_trace()
mstats()
and inspect mstats, I will get back the function that is unmocked.
However, if I run from k.stats import mstats from the breakpoint, I get back the mocked method that I am looking for.
How do I ensure that the mocked method gets called?
This is a pretty confusing concept, but the documentation of patch tries its best to explain it.
patch works by (temporarily) changing the object that a name points to with another one. There can be many names pointing to any individual object, so for patching to work you must ensure that you patch the name used by the system under test.
The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.
This is why you're able to observe the mocked object when you decide to inject it in; you're observing the patched reference where it's looked up at that moment.
The example does an okay job of explaining what's going on there, but I'll try to clarify.
Let's say that mstats lives in module stats. You're importing it through from stats import mstats in module use_stats.
You're going to want to mock it in use_stats, since that's its place of reference.
#patch('use_stats.mstats')
def test_stats(self, mstats):
pass

lazy load dictionary

I have a dictionary called fsdata at module level (like a global variable).
The content gets read from the file system. It should load its data once on the first access. Up to now it loads the data during importing the module. This should be optimized.
If no code accesses fsdata, the content should not be read from the file system (save CPU/IO).
Loading should happen, if you check for the boolean value, too:
if mymodule.fsdata:
... do_something()
Update: Some code already uses mymodule.fsdata. I don't want to change the other places. It should be variable, not a function. And "mymodule" needs to be a module, since it gets already used in a lot of code.
I think you should use Future/Promise like this https://gist.github.com/2935416
Main point - you create not an object, but a 'promise' about object, that behave like an object.
You can replace your module with an object that has descriptor semantics:
class FooModule(object):
#property
def bar(self):
print "get"
import sys
sys.modules[__name__] = FooModule()
Take a look at http://pypi.python.org/pypi/apipkg for a packaged approach.
You could just create a simple function that memoizes the data:
fsdata = []
def get_fsdata:
if not fsdata:
fsdata.append(load_fsdata_from_file())
return fsdata[0]
(I'm using a list as that's an easy way to make a variable global without mucking around with the global keyword).
Now instead of referring to module.fsdata you can just call module.get_fsdata().

Categories