I've been using the mock library to do some of my testing. It's been great so far, but there are some things that I haven't completely understand yet.
mock provides a nice way of patching an entire method using patch, and I could access the patched object in a method like so:
#patch('package.module')
def test_foo(self, patched_obj):
# ... call patched_obj here
self.assertTrue(patched_obj.called)
My question is, how do I access a patched object, if I use the patch decorator on an entire class?
For example:
#patch('package.module')
class TestPackage(unittest.TestCase):
def test_foo(self):
# how to access the patched object?
In this case, test_foo will have an extra argument, the same way as when you decorate the method. If your method is also patched, it those args will be added as well:
#patch.object(os, 'listdir')
class TestPackage(unittest.TestCase):
#patch.object(sys, 'exit')
def test_foo(self, sys_exit, os_listdir):
os_listdir.return_value = ['file1', 'file2']
# ... Test logic
sys_exit.assert_called_with(1)
The arguments order is determined by the order of the decorators calls. The method decorator is called first, so it appends the first argument. The class decorator is outer, so it will add a second argument. The same applies when you attach several patch decorators to the same test method or class (i.e. the outer decorator goes last).
Related
The following code shows the problem.
I can successfully patch object instance and static methods of this SomeClass
However, I can't seem to be able to patch classmethods.
Help much appreciated!
from contextlib import ExitStack
from unittest.mock import patch
class SomeClass:
def instance_method(self):
print("instance_method")
#staticmethod
def static_method():
print("static_method")
#classmethod
def class_method(cls):
print("class_method")
# --- desired patch side effect methods ----
def instance_method(self):
print("mocked instance_method")
def static_method():
print("mocked static_method")
def class_method(cls):
print("mocked class_method")
# --- Test ---
obj = SomeClass()
with ExitStack() as stack:
stack.enter_context(
patch.object(
SomeClass,
"instance_method",
side_effect=instance_method,
autospec=True
)
)
stack.enter_context(
patch.object(
SomeClass,
"static_method",
side_effect=static_method,
# autospec=True,
)
)
stack.enter_context(
patch.object(
SomeClass,
"class_method",
side_effect=class_method,
# autospec=True
)
)
# These work
obj.instance_method()
obj.static_method()
# This fails with TypeError: class_method() missing 1 required positional argument: 'cls'
obj.class_method()
General solution
A way to patch a classmethod would be to use new=classmethod(class_method) instead of side_effects=class_method.
This works pretty well in general.
Downside
Using new, the patched object isn't necessarily an instance of Mock, MagicMock, AsyncMock or PropertyMock anymore (During the rest of the answer i'll only reference Mock as all the others are subclasses of it).
It is only then an instance of these when you explicitly specify it to be one via e.g. new=Mock(...) or ommit the attribute completely.
That wouldn't be the case with the solution provided at the top of this answer.
So when you try to e.g. check if the function already got called using obj.class_method.assert_called(), it'll give an error saying that function has no attribute assert_called which is caused by the fact that the patched object isn't an instance of Mock, but instead a function.
Unfortunately I don't see any solution to this downside in that scenario at the moment
Concluded differences between new and side_effect:
new specifies what object to patch the target with (doesn't necessarily have to be an instance of Mock)
side_effect specifies the side_effect of the Mock instance that gets created when using patch without new
Also they don't play very well together, so only one of these can/should be used in the same patch(...).
I have a certain piece of code that looks like this:
# file1.py
from module import Object
def method():
o = Object("param1")
o.do_something("param2")
I have unittests that look like this:
#patch("file1.Object")
class TestFile(unittest.TestCase):
def test_call(self, obj):
...
I can do obj.assert_called_with() in the unittest to verify that the constructor was called with certain parameters. Is it possible to verify that obj.do_something was called with certain parameters? My instinct is no, as the Mock is fully encapsulated within Object, but I was hoping there might be some other way.
You can do this, because the arguments are passed to the mock object.
This should work:
#patch("file1.Object")
class TestFile:
def test_call(self, obj):
method()
obj.assert_called_once_with("param1")
obj.return_value.do_something.assert_called_once_with("param2")
obj.return_value is the Object instance (which is a MagickMock object with the Object spec), and do_something is another mock in that object that is called with the given parameter.
As long as you are just passing arguments to mock objects, the mock will record this and you can check it. What you don't have is any side effects from the real function calls - so if the original do_something would call another function, this cannot be checked.
When you mock an object, it will mock the methods inside the object aswell. Therefore you are able to see if obj.do_something has been called with certain parameters as obj.do_something.assert_called_with()
For more information regarding unittest mocking can be found at the python library wiki https://docs.python.org/3/library/unittest.mock.html
A perfect example of what you are asking exist within that wiki source:
>>> mock = Mock()
>>> mock.method(1, 2, 3, test='wow')
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_with(1, 2, 3, test='wow')
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_called_with
Regards, I see that you placed the the patching on the object, try placing it on the function instead, like:
class TestFile(unittest.TestCase):
#patch("file1.Object")
def test_call(self, obj):
...
I'm using the datashape Python package and registering a new type with the #datashape.discover.register decorator. I'd like to test that when I call datashape.discover on an object of the type I'm registering, it calls the function being decorated. I'd also like to do this with good unit testing principles, meaning not actually executing the function being decorated, as it would have side effects I don't want in the test. However, this isn't working.
Here's some sample code to demonstrate the problem:
myfile.py:
#datashape.discover.register(SomeType)
def discover_some_type(data)
...some stuff i don't want done in a unit test...
test_myfile.py:
class TestDiscoverSomeType(unittest.TestCase):
#patch('myfile.discover_some_type')
def test_discover_some_type(self, mock_discover_some_type):
file_to_discover = SomeType()
datashape.discover(file_to_discover)
mock_discover_some_type.assert_called_with(file_to_discover)
The issue seems to be that the function I want mocked is mocked in the body of the test, however, it was not mocked when it was decorated (i.e. when it was imported). The discover.register function essentially internally registers the function being decorated to look it up when discover() is called with an argument of the given type. Unfortunately, it seems to internally register the real function every time, and not the patched version I want, so it will always call the real function.
Any thoughts on how to be able to patch the function being decorated and assert that it is called when datashape.discover is called?
Here's a solution I've found that's only a little hacky:
sometype.py:
def discover_some_type(data):
...some stuff i don't want done in a unit test...
discovery_channel.py:
import sometype
#datashape.discover.register(SomeType)
def discover_some_type(data):
return sometype.discover_some_type(data)
test_sometype.py:
class TestDiscoverSomeType(unittest.TestCase):
#patch('sometype.discover_some_type')
def test_discover_some_type(self, mock_discover_some_type):
import discovery_channel
file_to_discover = SomeType()
datashape.discover(file_to_discover)
mock_discover_some_type.assert_called_with(file_to_discover)
The key is that you have to patch out whatever will actually do stuff before you import the module that has the decorated function that will register the patched function to datashape. This unfortunately means that you can't have your decorated function and the function doing the discovery in the same module (so things that should logically go together are now apart). And you have the somewhat hacky import-in-a-function in your unit test (to trigger the discover.register). But at least it works.
I have an class which decorates some methods using a decorator from another library. Specifically, the class subclasses flask-restful resources, decorates the http methods with httpauth.HTTPBasicAuth().login_required(), and does some sensible defaults on a model service.
On most subclasses I want the decorator applied; therefore I'd rather remove it than add it in the subclasses.
My thought is to have a private method which does the operations and a public method which is decorated. The effects of decoration can be avoided by overriding the public method to call the private one and not decorating this override. Mocked example below.
I am curious to know if there's a better way to do this. Is there a shortcut for 'cancelling decorators' in python that gives this effect?
Or can you recommend a better approach?
Some other questions have suitable answers for this, e.g. Is there a way to get the function a decorator has wrapped?. But my question is about broader design - i am interested in any pythonic way to run the operations in decorated methods without the effects of decoration. E.g. my example is one such way but there may be others.
def auth_required(fn):
def new_fn(*args, **kwargs):
print('Auth required for this resource...')
fn(*args, **kwargs)
return new_fn
class Resource:
name = None
#auth_required
def get(self):
self._get()
def _get(self):
print('Getting %s' %self.name)
class Eggs(Resource):
name = 'Eggs'
class Spam(Resource):
name = 'Spam'
def get(self):
self._get()
# super(Spam, self)._get()
eggs = Eggs()
spam = Spam()
eggs.get()
# Auth required for this resource...
# Getting Eggs
spam.get()
# Getting Spam
Flask-HTTPAuth uses functools.wraps in the login_required decorator:
def login_required(self, f):
#wraps(f)
def decorated(*args, **kwargs):
...
From Python 3.2, as this calls update_wrapper, you can access the original function via __wrapped__:
To allow access to the original function for introspection and other
purposes (e.g. bypassing a caching decorator such as lru_cache()),
this function automatically adds a __wrapped__ attribute to the
wrapper that refers to the function being wrapped.
If you're writing your own decorators, as in your example, you can also use #wraps to get the same functionality (as well as keeping the docstrings, etc.).
See also Is there a way to get the function a decorator has wrapped?
Another common option is to have the decorated function keep a copy of the original function that can be accessed:
def auth_required(fn):
def new_fn(*args, **kwargs):
print('Auth required for this resource...')
fn(*args, **kwargs)
new_fn.original_fn = fn
return new_fn
Now, for any function that has been decorated, you can access its original_fn attribute to get a handle to the original, un-decorated function.
In that case, you could define some type of dispatcher that either makes plain function calls (when you are happy with the decorator behavior) or makes calls to thing.original_fn when you prefer to avoid the decorator behavior.
Your proposed method is also a valid way to structure it, and whether my suggestion is "better" depends on the rest of the code you're dealing with, who needs to read it, and other kinds of trade-offs.
I am curious to know if there's a better way to do this. Is there a
shortcut for 'cancelling decorators' in python that gives this effect?
Use the undecorated library. It digs through all the decorators and returns just the original function. The docs should be self-explanatory, basically you just call: undecorated(your_decorated_function)
The Mock documentation describes a simple and elegant way of applying patches to all of the tests method inside a TestCase:
#patch('foo.bar')
#patch('foo.baz')
#patch('foo.quux')
#patch('foo.narf')
class FooTest(TestCase):
def test_foo(self, bar, baz, quux, narf):
""" foo """
self.assertTrue(False)
However, one issue I've encountered with this method is that if I'd like to call stop() on one of the patches inside one of the test methods, there doesn't appear to be anyway of getting a reference to the patcher object -- the only thing that is passed into the method is the mock objects, in this case bar, baz, quux, narf.
The only way I've found to solve this problem is to move to the pattern described in the Mock docs where the patchers are instantiated and started inside the setUp method of the TestCase and stopped inside the tearDown method. This fits my purpose, but adds a lot of extra boilerplate and isn't as elegant as the class decorator approach.
Is there another way to solve this problem?
1
Say you want to temporarily restore foo.narf in a method. foo.narf is, in the context of the decorated function, a MagicMock object. This object has a _mock_wraps attribute which will be invoked when the mock is called! So at the top of your module, _narf = foo.narf, and in your test case, foo.narf._mock_wraps = _narf.
The catch is that this will only pass through to the real function, not actually swap it back, which means that some test cases will fail (e.g. if they rely on the function object actually being "itself"). And if your mock has other attributes, that could interfere (I haven't tested much) because the passthrough call to _mock_wraps() comes at the bottom of a method that first considers the other properties of the mock.
2
The patch() decorator involves each patcher (separate copies per method) being added to a list called patchings which is a field of the method itself. I.e. you can access this list as self.test_foo.patchings, and go through to find the one you want.
However, start() and stop() are not actually called when you use patch() as a decorator, and the behavior gets tricky once you start reaching in and changing it. So I wrote this context manager.
class unpatch:
def __init__(self, name, method):
compare = patch(name)
self.patcher = next((
p for p in method.patchings
if p.target == compare.getter()
and p.attribute == compare.attribute
), None)
if self.patcher is None:
raise ValueError(name)
def __enter__(self):
self.patcher.__exit__()
def __exit__(self, *exc_info):
self.patcher.__enter__()
Inside your test case, you use it like this:
with unpatch('foo.narf', self.test_foo):
foo.narf()
Disclaimer: this is hacks.