- November 24, 2009
- monkeypatch testing
- 0 (after 0 ratings)
In test code, it is sometimes useful to monkeypatch a Django method to have stubbed out behavior, so that you can simplify data setup. Even with decent data setup you might want to avoid execution of Django code that is not the target of your test.
The code snippet shown here illustrates a technique to limit the scope of your monkeypatches. It uses the Python "with" statement, which was introduced in 2.5.
The key aspect of the "with" machinery is that you can set up an exit method that gets called even if the code inside the "with" raises an exception. This guarantees that your monkeypatch gets un-monkeyed before any other code gets called.
I don't recommend monkeypatches in production, but if you HAVE to resort to a monkeypatch, I definitely advise using "with" to limit their scope.
The examples on the left illustrate how to suppress versions of reverse() and timesince()--look at the import statements to see which ones I am talking about. Obviously, monkeypatching is not for the faint of the heart, as you need to be able to find the code to monkeypatch in Django source, and you need to be sure there aren't decorators at play.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
from django.core import urlresolvers from django.utils import timesince # set up a class to turn off reverse via monkeypatch...we do not care about its details class PatchReverse: def __enter__(self): self.old_method = urlresolvers.reverse urlresolvers.reverse = lambda view_name, args, kwargs, current_app: 'stub' def __exit__(self, type, value, traceback): urlresolvers.reverse = self.old_method # nor do we care about the details of timesince...note that we monkeypatch the one # from django.utils...it is harder to monkeypatch template filters due to django # registration mechanisms class PatchTimeSince: def __enter__(self): self.old_method = timesince.timesince timesince.timesince = lambda arg: 'stub' def __exit__(self, type, value, traceback): timesince.timesince = self.old_method with PatchReverse(): with PatchTimeSince(): from django.template.loader import render_to_string render_to_string('activity.html', dict(...))