Mocking __init__ of a class imported in file - python

I have a file copy_x_to_y.py which goes like this:
from abcd import F
def function_to_be_tested():
F()
in abcd.py file, i have something like this:
from xyz import XY
class F():
def __init__(self, arg1):
self.xy = XY(arg1)
I want to mock init of XY in my test case.
I have tried mocking F's init with:
def mock_func(*args, **kwargs):
pass
#patch('path/to/copy_x_to_y.F.__init__', mock_func)
def test():
assert function_to_be_tested() is None
but it always happens to call XY's init, resulting in error as its initialization calls
to connect with S3 with arg1. How to test this kind of structure?

What is the reason for wanting to mock __init__ of XY? Do you want it to return a specific object of XY, do you want to check if XY.__init__ was called with specific arguments or something else?
A possible solution to your problem would be to mock the entire class, but have it return a "normal" object. Here's an example:
>>> from unittest.mock import patch
>>> class MyClass:
... def __init__(self, val):
... self._val = val
... def foo(self):
... print(self._val)
...
>>> a = MyClass(1)
>>> a.foo()
1
>>> patcher = patch('__main__.MyClass', return_value=a)
>>> mock_class = patcher.start()
>>> b = MyClass(2) # This will return a.
>>> b.foo()
1
>>> mock_class.call_args_list
[call(2)]
>>> patcher.stop()
Which in your case would be:
from xyz import XY
from path/to/copy_x_to_y import function_to_be_tested
def test():
arg1 = ...
a = XY(arg1) # Has to be called before the patch to get a "normal" object.
with patch('xyz.XY', return_value=a) as mock_xy:
# Run funcion to be tested here and check results.
function_to_be_tested()
assert ...
Some side notes:
It is possible to mock __init__ directly though, if that's really what you need to do.
>>> def my_init(self, *args, **kwargs):
... self._val = 1
>>> patcher = patch.object(MyClass, '__init__', my_init)
>>> mock_init = patcher.start()
>>> a = MyClass(2)
>>> a.foo()
1
If you use the patch decorator, you have to supply the decorated function with one extra argument that is the mock of the class or object.
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch
#patch('path/to/SomeClass', ...)
def test(mock_class):
...
Also patch is typically (exclusively?) used to patch a class while patch.object is used to patch a member inside a class or module. https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch.object

Related

Python Mock - check if method was called from another class?

How can I check if method is called inside another method, when those methods are from different classes?
If they are from same class, I can do this:
from unittest import mock
class A():
def method_a(self):
pass
def method_b(self):
self.method_a()
a = A()
a.method_a = mock.MagicMock()
a.method_b()
a.method_a.assert_called_once_with()
But if method_a would be from different class, then it would raise AssertionError that it was not called.
How could I do same check if I would have these classes instead (and I want to check if method_b calls method_a)?:
class A():
def method_a(self):
pass
class B():
def method_b(self):
A().method_a()
You have to simply stub A within the same context as B, and validate against the way it would have been called. Example:
>>> class B():
... def method_b(self):
... A().method_a()
...
>>> A = mock.MagicMock()
>>> A().method_a.called
False
>>> b = B()
>>> b.method_b()
>>> A().method_a.called
True
>>>

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()
...

Assert that derived class methods are called in correct order

I'm trying to verify that the implementation of Base.run_this calls the methods of derived class (derived_method_[1st|2nd|3rd]) in correct order. As the output shows, the test is not working. How can I fix this?
class Base(object):
__metaclass__ = abc.ABCMeta
def __init__(self, parameters):
self.parameters = parameters;
#abc.abstractmethod
def must_implement_this(self):
return
def run_this(self):
self.must_implement_this()
if(self.parameters):
first = getattr(self, "derived_method_1st")
first()
second = getattr(self, "derived_method_2nd")
second()
third = getattr(self, "derived_method_3rd")
third()
class Derived(Base):
def must_implement_this(self):
pass
def derived_method_1st(self):
pass
def derived_method_2nd(self):
pass
def derived_method_3rd(self):
pass
mocked = MagicMock(wraps=Derived(True))
mocked.run_this()
mocked.assert_has_calls([call.derived_method_1st(), call.derived_method_2nd(), call.derived_method_3rd()])
Output
AssertionError: Calls not found.
Expected: [call.derived_method_1st(), call.derived_method_2nd(), call.derived_method_3rd()]
Actual: [call.run_this()]
wraps doesn't work well with instances. What happens here is that mocked.run_this returns a new mock object that 'wraps' Derived(True).run_this, where the latter is a bound method to the original Derived() instance.
As such, that method will call self.derived_method_* methods that are bound to that original instance, not to the mock.
You could patch in the run_this method on a spec mock instead:
mock = MagicMock(spec=Derived)
instance = mock()
instance.run_this = Derived.run_this.__get__(instance) # bind to mock instead
instance.parameters = True # adjust as needed for the test
instance.run_this()
Demo:
>>> mock = MagicMock(spec=Derived)
>>> instance = mock()
>>> instance.run_this = Derived.run_this.__get__(instance) # bind to mock instead
>>> instance.parameters = True # adjust as needed for the test
>>> instance.run_this()
>>> instance.mock_calls
[call.must_implement_this(),
call.derived_method_1st(),
call.derived_method_2nd(),
call.derived_method_3rd()]

List of functions calls in python tests

I have an object
class Obj:
def method1(self):
print 'method1'
def method2(self):
print 'method2'
def method3(self):
print 'method3'
and function
def do_something():
obj = Obj()
obj.method2()
obj.method1()
obj.method3()
And I want to write test which tests do_something and Obj object.
How can I receive list of methods which was called on obj without replacing (mocking) and changing obj behavior?
Something like
['method2', 'method1', 'method3']
Use the trace package. See docs: http://docs.python.org/2/library/trace.html
From the docs:
import sys
import trace
# create a Trace object, telling it what to ignore, and whether to
# do tracing or line-counting or both.
tracer = trace.Trace(
ignoredirs=[sys.prefix, sys.exec_prefix],
trace=0,
count=1)
# run the new command using the given tracer
tracer.run('main()')
# make a report, placing output in the current directory
r = tracer.results()
r.write_results(show_missing=True, coverdir=".")
You can create a generic Wrapper class which will encapsulate your object and track changes to it.
class Obj:
def method1(self):
print 'method1'
def method2(self):
print 'method2'
def method3(self):
print 'method3'
class Wrapper:
def __init__(self, wrapped):
self.calls = []
self._wrapped = wrapped
def __getattr__(self, n):
self.calls.append(n)
return getattr(self._wrapped, n)
By redefining __getattr__ we cause all attribute accessing on the wrapper to retrieve the attribute in the wrapped object. With the above defined I can do the following:
>>> obj = Obj()
>>> x = Wrapper(obj)
>>> x.calls
[]
>>> x.method2()
method 2
>>> x.method1()
method 1
>>> x.method3()
method 3
>>> x.calls
['method2', 'method1', 'method3']
>>> x.method1()
method 1
>>> x.method1()
method 1
>>> x.calls
['method2', 'method1', 'method3', 'method1', 'method1']
You can further improve __getattr__ in Wrapper to fit your needs. (record timestamps for the method calls, record output, log to a database, etc.)

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