How can I use patch as a method call? - python

The unittest docs state:
patch() acts as a function decorator, class decorator or a context
manager
I found the following way to also allow patching via a method call. You can then patch something by calling this method. For example, this is helpful for patching something in a setup method:
class MyTestCase(TestCase):
def patch(self, target, new):
p = patch(target, new)
p.start()
self.addCleanup(p.stop)
# ...
def setUp(self):
self.a = Mock()
self.patch("whatever.I.want.to.patch", self.a)
def test(self):
...
self.a.assert_called_once()
Is there a cleaner way of doing this? What is the reasoning for not providing this functionality out of the box?

Related

Mocking class attributes and methods with Python and Pytest [duplicate]

How do you mock a readonly property with mock?
I tried:
setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())
but the issue is that it then applies to all instances of the class... which breaks my tests.
Do you have any other idea? I don't want to mock the full object, only this specific property.
I think the better way is to mock the property as PropertyMock, rather than to mock the __get__ method directly.
It is stated in the documentation, search for unittest.mock.PropertyMock:
A mock intended to be used as a property, or other descriptor, on a class. PropertyMock provides __get__ and __set__ methods so you can specify a return value when it is fetched.
Here is how:
class MyClass:
#property
def last_transaction(self):
# an expensive and complicated DB query here
pass
def test(unittest.TestCase):
with mock.patch('MyClass.last_transaction', new_callable=PropertyMock) as mock_last_transaction:
mock_last_transaction.return_value = Transaction()
myclass = MyClass()
print myclass.last_transaction
mock_last_transaction.assert_called_once_with()
Actually, the answer was (as usual) in the documentation, it's just that I was applying the patch to the instance instead of the class when I followed their example.
Here is how to do it:
class MyClass:
#property
def last_transaction(self):
# an expensive and complicated DB query here
pass
In the test suite:
def test():
# Make sure you patch on MyClass, not on a MyClass instance, otherwise
# you'll get an AttributeError, because mock is using settattr and
# last_transaction is a readonly property so there's no setter.
with mock.patch(MyClass, 'last_transaction') as mock_last_transaction:
mock_last_transaction.__get__ = mock.Mock(return_value=Transaction())
myclass = MyClass()
print myclass.last_transaction
If the object whose property you want to override is a mock object, you don't have to use patch.
Instead, can create a PropertyMock and then override the property on the type of the mock. For example, to override mock_rows.pages property to return (mock_page, mock_page,):
mock_page = mock.create_autospec(reader.ReadRowsPage)
# TODO: set up mock_page.
mock_pages = mock.PropertyMock(return_value=(mock_page, mock_page,))
type(mock_rows).pages = mock_pages
In case you are using pytest along with pytest-mock, you can simplify your code and also avoid using the context manger, i.e., the with statement as follows:
def test_name(mocker): # mocker is a fixture included in pytest-mock
mocked_property = mocker.patch(
'MyClass.property_to_be_mocked',
new_callable=mocker.PropertyMock,
return_value='any desired value'
)
o = MyClass()
print(o.property_to_be_mocked) # this will print: any desired value
mocked_property.assert_called_once_with()
Probably a matter of style but in case you prefer decorators in tests, #jamescastlefield's answer could be changed to something like this:
class MyClass:
#property
def last_transaction(self):
# an expensive and complicated DB query here
pass
class Test(unittest.TestCase):
#mock.patch('MyClass.last_transaction', new_callable=PropertyMock)
def test(self, mock_last_transaction):
mock_last_transaction.return_value = Transaction()
myclass = MyClass()
print myclass.last_transaction
mock_last_transaction.assert_called_once_with()
If you need your mocked #property to rely on the original __get__, you can create your custom MockProperty
class PropertyMock(mock.Mock):
def __get__(self, obj, obj_type=None):
return self(obj, obj_type)
Usage:
class A:
#property
def f(self):
return 123
original_get = A.f.__get__
def new_get(self, obj_type=None):
return f'mocked result: {original_get(self, obj_type)}'
with mock.patch('__main__.A.f', new_callable=PropertyMock) as mock_foo:
mock_foo.side_effect = new_get
print(A().f) # mocked result: 123
print(mock_foo.call_count) # 1
If you don't want to test whether or not the mocked property was accessed you can simply patch it with the expected return_value.
with mock.patch(MyClass, 'last_transaction', Transaction()):
...
I was directed to this question because I wanted to mock the Python version in a test. Not sure whether this is quite relevant to this question, but sys.version is obviously read-only (... though technically an "attribute" rather than a "property", I suppose).
So, after perusing this place and trying some stupidly complicated stuff I realised the answer was simplicity itself:
with mock.patch('sys.version', version_tried):
if version_tried == '2.5.2':
with pytest.raises(SystemExit):
import core.__main__
_, err = capsys.readouterr()
assert 'FATAL' in err and 'too old' in err
... might help someone.

How to externalize the decorator in python flask application

I have written a python Flask application, which has a class and methods as below.
class PythonSample:
def method1():
pass # does something
def method2():
pass # does something
Now I have written another class which has decorator functions as below.
class PythonAuth:
def oauthAuth():
pass
Now I'm wiring oauthAuth decorator for all the methods of PythonSample class as below
import oauthAuth from PythonAuth
class PythonSample
#oauthAuth
def method1():
pass # does something
#oauthAuth
def method2():
pass # does something
Applying decorator at each method works fine.
Question: Instead of applying oauthAuth decorator to each of the methods. Is there a way to configure in python, as apply oauthAuth decorator to all the methods in a class and exclude certain methods.
Something like include auth for certain URLs and exclude authentication for certain urls
Please ignore the syntax of the python code here.
You can use a class decorator plus some magic.
Decorating Functions
Assume you have a decorator that just logs a string before calling the function.
def log(func):
def logged_func(*args, **kwargs):
print('logged')
func(*args, **kwargs)
return logged_func
Decorating classes
You can use the same trick, but with a class. log_all is a class decorator, cls is a class type. We use vars to walk the class dictionary, and look for methods by using callable(v). Decorate the method with log(v) and use setattr to change the cls definition to the new decorated method. Just like function decorators, return the class in the end.
def log_all(cls):
for k, v in vars(cls).items():
if callable(v):
setattr(cls, k, log(v))
return cls
I am ignoring k essentially, but k is the method name, you could leverage it to achieve your usage scenario.
Full code
Here is a full example, that should make some sense now.
def log(func):
def logged_func(*args, **kwargs):
print('logged')
func(*args, **kwargs)
return logged_func
def log_all(cls):
for k, v in vars(cls).items():
if callable(v):
setattr(cls, k, log(v))
return cls
#log_all
class A:
def method(self):
pass
Every method in class A should be decorated with the log decorator.
>>> a = A()
>>> a.method()
logged

How can I mock/patch a decorator in python?

WORKAROUND:
I was unable to patch the decorator and still have access to the wrapped function. but the workaround for me to test the function wrapped in a decorator was the following:
def un_some_method(self):
...
some_method = some_var.some_decorator('somestring')(un_some_method)
This still decorates my function, but gives me access to the function if I wish to test it, reuse it elsewhere...
This is the problem:
I have a module, which has a class in it, and variables that instantiate a class which expose a decorator.
Then within the class inside my module, I use this variable with the instance of my class and decorate a method in my class... To be clear, lets look at some code:
some_var = ClassX()
class SomeClass(object):
#some_var.some_decorator('somestring')
def some_method(self):
...
in my test I want to test the code within some_method and I don't care about the decorator... although my attempts to mock the decorator out was useless... below is some things I tried:
#patch('path_to_classx.ClassX.some_decorator')
#patch('path_to_someclassmodule.some_var')
#patch('path_to_someclassmodule.ClassX')
none of the attempts above prevented the function from being decorated... any idea why?
One option is to patch some_var after you have instantiated it, replacing some_decorator with an identity function so that the decorated method remains unchanged.
import mock
class ClassX(object):
def some_decorator(self, f):
def g(*args, **kwargs):
print("Hello")
return f(*args, **kwargs)
return g
some_var = ClassX()
with mock.patch.object(some_var, 'some_decorator', side_effect=lambda f: f):
class SomeClass(object):
#some_var.some_decorator
def some_method(self):
print "World"
SomeClass().some_method()

mocking in TestCase.setUp()

I want to mock all test methods of a TestCase.
My first try to use TestCase.setUp() did not work, since setUp() finishes before the test methods gets executed.
I can't mock the real test method with this inside setUp():
with mock.patch(...):
do_something()
I guess I am missing something.
How to use mock.patch() for all methods of a test case?
with mock.patch() is a context manager, the patch is unapplied when the context ends, and the context ends at the end of the block of code.
That means that the patches are unapplied again when setUp() ends.
Your options are to either use #mock.patch() as a class decorator or to use the start and stop methods on the patchers.
Using #mock.patch() as a class decorator has the same effect as applying it as a decorator to each and every test method:
#mock.patch('module.ClassName')
class TestFoo(unittest.TestCase):
def setUp(self):
# ...
def test_one(self, class_mock):
# ...
def test_two(self, class_mock):
# ...
Here both test_one and test_two are passed in a mock object because the #mock.patch() class decorator has found all test methods and decorated them.
Using the start and stop methods lets you apply and unapply patches in the setUp and tearDown methods:
class TestFoo(unittest.TestCase):
def setUp(self):
self.patch1 = mock.patch(...)
self.patch1.start()
def tearDown(self):
self.patch1.stop()
Here patch1 is started on set-up, and stopped again when the test is torn down. This acts just like the context manager, but instead hooks into the test boundaries.
Instead of using a tearDown, you can also register the patch.stop() as a cleanup function with TestCase.addCleanup():
class TestFoo(unittest.TestCase):
def setUp(self):
patch1 = mock.patch(...)
patch1.start()
self.addCleanup(patch1.stop)
You can use mock.patch.start, mock.patch.stop. (See patch methods: start and stop).
For example:
class MyTest(TestCase):
def setUp(self):
self.patcher = mock.patch('...')
self.MockClass = self.patcher.start()
def tearDown(self):
self.patcher.stop()
def test_something(self):
....
The most general solution, which works for all context managers is:
import unittest
class TCase(unittest.TestCase):
def setUp(self):
self.cm = mock.path(...)
self.cm.__enter__()
def test1(self):
...
def tearDown(self):
self.cm.__exit__(None, None, None)

Mocking a class method that is used via an instance

I'm trying to patch a class method using mock as described in the documentation. The Mock object itself works fine, but its methods don't: For example, their attributes like call_count aren't updated, even though the method_calls attribute of the class Mock object is. More importantly, their return_value attribute is ignored:
class Lib:
"""In my actual program, a module that I import"""
def method(self):
return "real"
class User:
"""The class I want to test"""
def run(self):
l = Lib()
return l.method()
with patch("__main__.Lib") as mock:
#mock.return_value = "bla" # This works
mock.method.return_value = "mock"
u = User()
print(u.run())
>>>
mock
<MagicMock name='Lib().method()' id='39868624'>
What am I doing wrong here?
EDIT: Passing a class Mock via the constructor doesn't work either, so this is not really related to the patch function.
I have found my error: In order to configure the methods of my mock's instances, I have to use mock().method instead of mock.method.
class Lib:
"""In my actual program, a module that I import"""
def method(self):
return "real"
class User:
"""The class I want to test"""
def run(self):
l = Lib()
return l.method()
with patch("__main__.Lib") as mock:
#mock.return_value = "bla" # This works
mock().method.return_value = "mock"
u = User()
print(u.run())
from mock import *
class Lib:
"""In my actual program, a module that I import"""
def method(self):
return "real"
class User:
"""The class I want to test"""
def run(self, m):
return m.method()
with patch("__main__.Lib") as mock:
#mock.return_value = "bla" # This works
mock.method.return_value = "mock"
print User().run(mock)
I mock classmethods like this:
def raiser(*args, **kwargs):
raise forms.ValidationError('foo')
with mock.patch.object(mylib.Commands, 'my_class_method', classmethod(raiser)):
response=self.admin_client.get(url, data=dict(term='+1000'))

Categories