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

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
>>>

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)

Mocking __init__ of a class imported in file

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

How to redefine python3 MagicMock magic method?

I need to redefine MagicMock's magic method in the child class and then use child class for testing, so I've tried:
from unittest.mock import MagicMock
class MockForTest(MagicMock):
def __lt__(self):
return self
t1 = MockForTest()
print(t1.__lt__()) # prints NotImplemented
But why NotImplemented? I know I can do it:
class A():
def __lt__(self):
return NotImplemented
class B(A):
def __lt__(self):
return self
b = B()
print(b.__lt__()) # prints <__main__.B object at 0x000001D6EE77B2B0>
So, I can't redefine MagicMock's magic methods?
You can use instead
t1.__lt__.return_value = t1
From what I can understand from https://github.com/python/cpython/blob/4002d5dbf4c058bbf2462f9f5dea057956d1caff/Lib/unittest/mock.py#L1834 MagicMixin remove all other magic methods.

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

Check if a function uses #classmethod

TL;DR How do I find out whether a function was defined using #classmethod or something with the same effect?
My problem
For implementing a class decorator I would like to check if a method takes the class as its first argument, for example as achieved via
#classmethod
def function(cls, ...):
I found a solution to check for #staticmethod via the types module (isinstance(foo, types.UnboundMethodType) is False if the foo is static, see here), but did not find anything on how to do so for #classmethod
Context
What I am trying to do is something along the lines of
def class_decorator(cls):
for member in cls.__dict__:
if (isclassmethod(getattr(cls, member))):
# do something with the method
setattr(cls, member, modified_method)
return cls
and I do not know how to implement what I called isclassmethod in this example
If the object is a method object, and so has a method.__self__ attribute, and that attribute is the class you got the attribute from, then it'll take the class as the first argument. It has been bound to the class.
Note that you already have a bound object at this point, so you don't need to pass in the class again, unless you first extract the original function from method.__func__.
Here is an illustration, the class Foo has a class method bar and a regular method baz, which is not bound when you access it directly on the class:
>>> class Foo:
... #classmethod
... def bar(cls):
... pass
... def baz(self):
... pass
...
>>> Foo.baz
<function Foo.baz at 0x1097d1e18>
>>> Foo.bar
<bound method Foo.bar of <class '__main__.Foo'>>
>>> Foo.bar.__self__
<class '__main__.Foo'>
>>> Foo.bar.__self__ is Foo
True
Calling Foo.bar() automatically passes in Foo.bar.__self__ as the first argument.
If you need to test such methods, use inspect.ismethod(), and if that returns True test the __self__ attribute:
import inspect
if inspect.ismethod(cls.method) and cls.method.__self__ is cls:
# method bound to the class, e.g. a classmethod
This should work for any custom descriptors that work like classmethod does, as well.
If you need to know with certainty that the method was produced by a classmethod object, you'll need to look up the attributes directly in the class namespace (cls.__dict__ or vars(cls)), and do so in each class in the class hierarchy in method resolution order:
def isclassmethod(method):
bound_to = getattr(method, '__self__', None)
if not isinstance(bound_to, type):
# must be bound to a class
return False
name = method.__name__
for cls in bound_to.__mro__:
descriptor = vars(cls).get(name)
if descriptor is not None:
return isinstance(descriptor, classmethod)
return False
and a full test of the above two approaches using a base class and a derived class, with a custom descriptor that binds a function the same way a classmethod would, but is not, itself, a classmethod:
>>> class notclassmethod:
... def __init__(self, f):
... self.f = f
... def __get__(self, _, typ=None):
... return self.f.__get__(typ, typ)
...
>>> class Base:
... #classmethod
... def base_cm(cls): pass
... #notclassmethod
... def base_ncm(cls): pass
... def base_m(self): pass
...
>>> class Derived(Base):
... #classmethod
... def derived_cm(cls): pass
... #notclassmethod
... def derived_ncm(cls): pass
... def derived_m(self): pass
...
>>> inspect.ismethod(Derived.base_cm) and Derived.base_cm.__self__ is Derived
True
>>> inspect.ismethod(Derived.base_ncm) and Derived.base_ncm.__self__ is Derived
True
>>> inspect.ismethod(Derived.base_m) and Derived.base_m.__self__ is Derived
False
>>> inspect.ismethod(Derived.derived_cm) and Derived.derived_cm.__self__ is Derived
True
>>> inspect.ismethod(Derived.derived_ncm) and Derived.derived_ncm.__self__ is Derived
True
>>> inspect.ismethod(Derived.derived_m) and Derived.derived_m.__self__ is Derived
False
>>> isclassmethod(Derived.base_cm)
True
>>> isclassmethod(Derived.base_ncm)
False
>>> isclassmethod(Derived.base_m)
False
>>> isclassmethod(Derived.derived_cm)
True
>>> isclassmethod(Derived.derived_ncm)
False
>>> isclassmethod(Derived.derived_m)
False
The isclassmethod() function correctly distinguishes between the classmethod and notclassmethod descriptors.
Historical note: this answer included references to Python 2, but with Python 2 having reached EOL were removed as no longer relevant.
You should use inspect.ismethod. It works because classmethod binds the function to the class object. See the following code:
>>> class Foo:
... #classmethod
... def bar():
... pass
... def baz():
... pass
...
>>> Foo.bar
<bound method type.bar of <class '__main__.Foo'>>
>>> Foo.baz
<function Foo.baz at 0x0000000002CCC1E0>
>>> type(Foo.bar)
<class 'method'>
>>> type(Foo.baz)
<class 'function'>
>>> import inspect
>>> inspect.ismethod(Foo.bar)
True
>>> inspect.ismethod(Foo.baz)
False
class Foo(object):
#classmethod
def baaz(cls):
print "baaz"
isinstance(Foo.__dict__["baaz"], classmethod)
None of the answers address the problem of identifying whether a method is decorated with class method from an instance of the class. Following code explores the class dict of an instance to distinguish between classmethod from other methods.
class MyClass(object):
#classmethod
def class_method(cls):
pass
def instance_method(self):
pass
#staticmethod
def static_method():
pass
def blas(): pass
t = MyClass()
isinstance(t.__class__.__dict__[t.class_method.__name__], classmethod) # True
isinstance(t.__class__.__dict__[t.static_method.__name__], classmethod) # False
isinstance(t.__class__.__dict__[t.instance_method.__name__], classmethod) # False
isinstance(t.__class__.__dict__[t.blas.__name__], classmethod) # False
This will work for both Python 2 and 3.
This works for me:
def is_classmethod(method):
"""
Is method a classmethod?
"""
return isinstance(getattr(method, '__self__', None), type)
It basically tests if method.__self__ exists and is a class, as in Martijn's answer, but does not require access to the class itself.

Categories

Resources