How does one use pytest monkeypatch to patch a class - python

I would like to use [pytest monkeypatch][1] to mock a class which is imported
into a separate module. Is this actually possible, and if so how does one do it? It seems like I have not seen an example for this exact situation. Suppose you have app with and imported class A in something.py
from something import A #Class is imported
class B :
def __init__(self) :
self.instance = A() #class instance is created
def f(self, value) :
return self.instance.g(value)
inside my test.py I want to mock A inside B
from something import B
#this is where I would mock A such that
def mock_A :
def g(self, value) :
return 2*value
#Then I would call B
c = B()
print(c.g(2)) #would be 4
I see how monkeypatch can be used to patch instances of classes, but how is it done for classes that have not yet been instantiated? Is it possible? Thanks!
[1]: https://docs.pytest.org/en/latest/monkeypatch.html

tested this, works for me:
def test_thing(monkeypatch):
def patched_g(self, value):
return value * 2
monkeypatch.setattr(A, 'g', patched_g)
b = B()
assert b.f(2) == 4

Related

Python, can't assert if method called from another class

I have the following code in Python:
class A():
def doSomething(self, bClass):
print(bClass.theThing)
class B():
def __init__(self, theThing):
self.theThing = theThing
def foo():
a = A()
b = B("that thing")
a.doSomething(b)
I have those classes and the function foo() stored in testing.py and I want to test that the A's method was called with:
import testing, unittest
from unittest.mock import patch
class TheTestClass(unittest.TestCase):
def test(self):
with patch('testing.A.doSomething') as do:
testing.foo()
do.assert_any_call()
But I always get 'doSomething() call not found'. I would be happier if I could understand why but at this point anything is welcome
After many hours I started figuring this out.
Like jwjhdev said assert_called_with() expects something and in my case a class but so does assert_any_call(). For some reason I was thinking assert_any_call() would just work but what I was thinking was assert_called() which just works without arguments. In the end I figured it out by adding a return b to the foo() function and:
def foo():
a = A()
b = B("that thing")
a.doSomething(b)
return b
class TheTestClass(unittest.TestCase):
def test(self):
with patch('testing.A.doSomething') as do:
b = testing.foo()
do.assert_any_call(b)

How to mock a method of an imported instance

I am writing unit test for some python modules. However, I cannot work out a way to mock a method of an imported instance. Below is the python modules that I want to test.
--bar.py--
class A():
#classmethod
def method_1(self):
...
a = A()
--foo.py--
from bar import a
class B():
#classmethod
def b(cls):
if a.method_1():
return True
else:
return False
--test_foo.py--
from foo import B
class TestB(TestCase):
#patch('foo.a.method_1')
def test_b(self, mock_method_1):
mock_method_1.return_value = True
test_b = B.b()
...
This does not work. My test case is still calling original method_1 instead of the one I mocked.
Use the patch.object decorator instead. It patches an object's attributes instead of patching a global method.
If that doesn't work, try patching bar.a instead of foo.a, but I don't think that's your problem here.
Update
The question changed to a class method, so I think this will work:
--test_foo.py--
from foo import B
class TestB(TestCase):
#patch('bar.A.method_1')
def test_b(self, mock_method_1):
mock_method_1.return_value = True
test_b = B.b()
...

How to capture field value when a method of a class is called in Python?

I have a weird 3rd party library that requires me doing the following (A is imported from that library):
def foo(my_props):
a = A()
a.props = my_props
a.post()
So what I want is to make sure that when a.post() is called, its props are set correctly. Note that this is a greatly simplified example so it's apparent it would be easy to mock foo instead. Unfortunately there is much more into it (like my_props may be modified in foo before calling a.post).
Worth noting that from looking into the source code of that import, props is not a class property. It's a simple dict, class field, set something like self.props = ... at random places in the class A.
So how can I setup my mocking to accomplish this feast? I am not even interested in post itself. I need to know how many times delete is called and what values props were set at that time.
EDIT: re-enforcing #hspandher response, ended up doing the following because unfortunately call_args stayed empty, which I hoped I could have analyzed after the call.
#mock.patch('A.props', return_value=mock.PropertyMock())
def test_foo(self, mock_props):
call_args = []
def capture(*args, **kwargs):
call_args.append(args)
mock_props.__set__ = functools.partial(capture)
a = A()
a.foo()
# analyze call_args...
For this you don't even need mocking, lets say foo is defined in file code.py, in test file code should be like this from code import A A.props = <mockvalue> and then your testing code. But if you to want to do something little more sophisticated like mocking post , I suggest you use python mock or fudge library
If you can initialize a outside of foo and pass as argument so the actual call looks like this:
def foo(my_props, obj):
obj.props = my_props
obj.post()
foo({'bar': 'baz'}, a)
Then you could test it like this:
def test_foo():
a = mock.create_autospec(A)
foo(1, a)
assert a.props == 1
assert a.mock_calls == [mock.call.post()]
Checking if props are set before calling post is a little more work.
You will need to convert foo to a class Foo:
class Foo(object):
def foo(self, my_props, obj):
self._setup_props(my_props, obj)
self._post(obj)
def _setup_props(self, my_props, obj):
obj.props = my_props
def _post(self, obj):
obj.post()
Now you can mock Foo itself and it's foo method to check the mock_calls order:
>>> f = mock.Mock(spec=Foo, foo=Foo.foo)
>>> f.foo(f, 1, 2)
>>> f.mock_calls
[call._setup_props(1, 2), call._post(2)]

Using python's mock patch.object to change the return value of a method called within another method

Is it possible to mock a return value of a function called within another function I am trying to test? I would like the mocked method (which will be called in many methods I'm testing) to returned my specified variables each time it is called. For example:
class Foo:
def method_1():
results = uses_some_other_method()
def method_n():
results = uses_some_other_method()
In the unit test, I would like to use mock to change the return value of uses_some_other_method() so that any time it is called in Foo, it will return what I defined in #patch.object(...)
There are two ways you can do this; with patch and with patch.object
Patch assumes that you are not directly importing the object but that it is being used by the object you are testing as in the following
#foo.py
def some_fn():
return 'some_fn'
class Foo(object):
def method_1(self):
return some_fn()
#bar.py
import foo
class Bar(object):
def method_2(self):
tmp = foo.Foo()
return tmp.method_1()
#test_case_1.py
import bar
from mock import patch
#patch('foo.some_fn')
def test_bar(mock_some_fn):
mock_some_fn.return_value = 'test-val-1'
tmp = bar.Bar()
assert tmp.method_2() == 'test-val-1'
mock_some_fn.return_value = 'test-val-2'
assert tmp.method_2() == 'test-val-2'
If you are directly importing the module to be tested, you can use patch.object as follows:
#test_case_2.py
import foo
from mock import patch
#patch.object(foo, 'some_fn')
def test_foo(test_some_fn):
test_some_fn.return_value = 'test-val-1'
tmp = foo.Foo()
assert tmp.method_1() == 'test-val-1'
test_some_fn.return_value = 'test-val-2'
assert tmp.method_1() == 'test-val-2'
In both cases some_fn will be 'un-mocked' after the test function is complete.
Edit:
In order to mock multiple functions, just add more decorators to the function and add arguments to take in the extra parameters
#patch.object(foo, 'some_fn')
#patch.object(foo, 'other_fn')
def test_foo(test_other_fn, test_some_fn):
...
Note that the closer the decorator is to the function definition, the earlier it is in the parameter list.
This can be done with something like this:
# foo.py
class Foo:
def method_1():
results = uses_some_other_method()
# testing.py
from mock import patch
#patch('Foo.uses_some_other_method', return_value="specific_value"):
def test_some_other_method(mock_some_other_method):
foo = Foo()
the_value = foo.method_1()
assert the_value == "specific_value"
Here's a source that you can read: Patching in the wrong place
Let me clarify what you're talking about: you want to test Foo in a testcase, which calls external method uses_some_other_method. Instead of calling the actual method, you want to mock the return value.
class Foo:
def method_1():
results = uses_some_other_method()
def method_n():
results = uses_some_other_method()
Suppose the above code is in foo.py and uses_some_other_method is defined in module bar.py. Here is the unittest:
import unittest
import mock
from foo import Foo
class TestFoo(unittest.TestCase):
def setup(self):
self.foo = Foo()
#mock.patch('foo.uses_some_other_method')
def test_method_1(self, mock_method):
mock_method.return_value = 3
self.foo.method_1(*args, **kwargs)
mock_method.assert_called_with(*args, **kwargs)
If you want to change the return value every time you passed in different arguments, mock provides side_effect.
To add to Silfheed's answer, which was useful, I needed to patch multiple methods of the object in question. I found it more elegant to do it this way:
Given the following function to test, located in module.a_function.to_test.py:
from some_other.module import SomeOtherClass
def add_results():
my_object = SomeOtherClass('some_contextual_parameters')
result_a = my_object.method_a()
result_b = my_object.method_b()
return result_a + result_b
To test this function (or class method, it doesn't matter), one can patch multiple methods of the class SomeOtherClass by using patch.object() in combination with sys.modules:
#patch.object(sys.modules['module.a_function.to_test'], 'SomeOtherClass')
def test__should_add_results(self, mocked_other_class):
mocked_other_class().method_a.return_value = 4
mocked_other_class().method_b.return_value = 7
self.assertEqual(add_results(), 11)
This works no matter the number of methods of SomeOtherClass you need to patch, with independent results.
Also, using the same patching method, an actual instance of SomeOtherClass can be returned if need be:
#patch.object(sys.modules['module.a_function.to_test'], 'SomeOtherClass')
def test__should_add_results(self, mocked_other_class):
other_class_instance = SomeOtherClass('some_controlled_parameters')
mocked_other_class.return_value = other_class_instance
...

Mocking out methods on any instance of a python class

I want to mock out methods on any instance of some class in the production code in order to facilitate testing. Is there any library in Python which could facilitate this?
Basically, I want to do the following, but in Python (the following code is Ruby, using the Mocha library):
def test_stubbing_an_instance_method_on_all_instances_of_a_class
Product.any_instance.stubs(:name).returns('stubbed_name')
assert_equal 'stubbed_name', SomeClassThatUsesProduct.get_new_product_name
end
The important thing to note from above is that I need to mock it out on the class level, since I'm actually need to mock out methods on an instance created by the thing I'm testing.
Use Case:
I have a class QueryMaker which calls a method on an instance of RemoteAPI. I want to mock out the RemoteAPI.get_data_from_remote_server method to return some constant. How do I do this inside a test without having to put a special case within the RemoteAPI code to check for what environment it's running in.
Example of what I wanted in action:
# a.py
class A(object):
def foo(self):
return "A's foo"
# b.py
from a import A
class B(object):
def bar(self):
x = A()
return x.foo()
# test.py
from a import A
from b import B
def new_foo(self):
return "New foo"
A.foo = new_foo
y = B()
if y.bar() == "New foo":
print "Success!"
Needing to mock out methods when testing is very common and there are lots of tools to help you with it in Python. The danger with "monkey patching" classes like this is that if you don't undo it afterwards then the class has been modified for all other uses throughout your tests.
My library mock, which is one of the most popular Python mocking libraries, includes a helper called "patch" that helps you to safely patch methods or attributes on objects and classes during your tests.
The mock module is available from:
http://pypi.python.org/pypi/mock
The patch decorator can be used as a context manager or as a test decorator. You can either use it to patch out with functions yourself, or use it to automatically patch with Mock objects that are very configurable.
from a import A
from b import B
from mock import patch
def new_foo(self):
return "New foo"
with patch.object(A, 'foo', new_foo):
y = B()
if y.bar() == "New foo":
print "Success!"
This handles the unpatching for you automatically. You could get away without defining the mock function yourself:
from mock import patch
with patch.object(A, 'foo') as mock_foo:
mock_foo.return_value = "New Foo"
y = B()
if y.bar() == "New foo":
print "Success!"
Mock is the way to do it, alright.
It can be a bit tricky to make sure you're patching the instance method on any instances created from the class.
# a.py
class A(object):
def foo(self):
return "A's foo"
# b.py
from a import A
class B(object):
def bar(self):
x = A()
return x.foo()
# test.py
from a import A
from b import B
import mock
mocked_a_class = mock.Mock()
mocked_a_instance = mocked_a_class.return_value
mocked_a_instance.foo.return_value = 'New foo'
with mock.patch('b.A', mocked_a_class): # Note b.A not a.A
y = B()
if y.bar() == "New foo":
print "Success!"
Referenced in the docs, at the para starting "To configure return values on methods of instances on the patched class..."
Easiest way is probably to use a class method. You really should use an instance method, but it's a pain to create those, whereas there's a built-in function that creates a class method. With a class method, your stub will get a reference to the class (rather than the instance) as the first argument, but since it's a stub this probably doesn't matter. So:
Product.name = classmethod(lambda cls: "stubbed_name")
Note that the signature of the lambda must match the signature of the method you're replacing. Also, of course, since Python (like Ruby) is a dynamic language, there is no guarantee that someone won't switch out your stubbed method for something else before you get your hands on the instance, though I expect you will know pretty quickly if that happens.
Edit: On further investigation, you can leave out the classmethod():
Product.name = lambda self: "stubbed_name"
I was trying to preserve the original method's behavior as closely as possible, but it looks like it's not actually necessary (and doesn't preserve the behavior as I'd hoped, anyhow).
I don't know Ruby quite well enough to tell exactly what you're trying to do, but check out the __getattr__ method. If you define it in your class, Python will call it when code tries to access any attribute of your class that isn't otherwise defined. Since you want it to be a method, it will need to create a method on the fly that it returns.
>>> class Product:
... def __init__(self, number):
... self.number = number
... def get_number(self):
... print "My number is %d" % self.number
... def __getattr__(self, attr_name):
... return lambda:"stubbed_"+attr_name
...
>>> p = Product(172)
>>> p.number
172
>>> p.name()
'stubbed_name'
>>> p.get_number()
My number is 172
>>> p.other_method()
'stubbed_other_method'
Also note that __getattr__ needs to not use any other undefined attributes of your class, or else it will be infinitely recursive, calling __getattr__ for the attribute that doesn't exist.
... def __getattr__(self, attr_name):
... return self.x
>>> p.y
Traceback (most recent call last):
#clipped
RuntimeError: maximum recursion depth exceeded while calling a Python object
If this is something you only want to do from your test code, not the production code, then put your normal class definition in the production code file, then in the test code define the __getattr__ method (unbound), and then bind it to the class you want:
#production code
>>> class Product:
... def __init__(self, number):
... self.number = number
... def get_number(self):
... print "My number is %d" % self.number
...
#test code
>>> def __getattr__(self, attr):
... return lambda:"stubbed_"+attr_name
...
>>> p = Product(172)
>>> p.number
172
>>> p.name()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
AttributeError: Product instance has no attribute 'name'
>>> Product.__getattr__ = __getattr__
>>> p.name()
'stubbed_name'
I'm not sure how this would react with a class that was already using __getattribute__ (as opposed to __getattr__, __getattribute__ is called for all attributes whether or not they exist).
If you only want to do this for specific methods that already exist, then you could do something like:
#production code
>>> class Product:
... def __init__(self, number):
... self.number = number
... def get_number(self):
... return self.number
...
>>> p = Product(172)
>>> p.get_number()
172
#test code
>>> def get_number(self):
... return "stub_get_number"
...
>>> Product.get_number = get_number
>>> p.get_number()
'stub_get_number'
Or if you really wanted to be elegant, you could create a wrapper function to make doing multiple methods easy:
#test code
>>> import functools
>>> def stubber(fn):
... return functools.wraps(fn)(lambda self:"stub_"+fn.__name__)
...
>>> Product.get_number = stubber(Product.get_number)
>>> p.get_number()
'stub_get_number'
#Orignal Class definition - path "module.Product"
class Product:
def method_A(self):
# do something
pass
def method_B(self):
self.random_attr = 1
#Test case
from module import Product
class MockedProduct(Product):
def method_B(self):
self.random_attr = 2
with mock.patch('module.Product', new=MockedProduct):
#Write test case logic here
#Now method_B function call on product class instance should return 2
#instead of 1
minimal reproducible example using pytest and monkeypatch
# a.py
class A(object):
def foo(self):
return "A's foo"
# b.py
from a import A
class B(object):
def bar(self):
x = A()
return x.foo()
# test_ab.py
import pytest
from a import A
from b import B
def new_foo(self):
return "New foo"
def test_mock_instance_method(monkeypatch):
y = B()
print(y.bar())
monkeypatch.setattr(A, 'foo', new_foo)
print(y.bar())
gives you
$ pytest -rP .
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /home/user/220225so
plugins: cov-3.0.0
collected 1 item
test_ab.py . [100%]
==================================== PASSES ====================================
__________________________ test_mock_instance_method ___________________________
----------------------------- Captured stdout call -----------------------------
A's foo
New foo
============================== 1 passed in 0.01s ===============================
$

Categories

Resources