I am new to mock in Python. I want to know how to replace (mock) a class method while testing with another one, knowing that the original just changes some attributes of self without returning any value. For example:
def some_method(self):
self.x = 4
self.y = 6
So here I can't just change the return_value of the mock. I tried to define a new function (that should replace the original) and give it as side_effect to the mock. But how can I make the mocking function change attributes of the object in the class.
Here is my code:
#patch('path.myClass.some_method')
def test_this(self,someMethod):
def replacer(self):
self.x = 5
self.y = 16
some_method.side_effect = replacer
So how does Python now understands the self argument of replacer? Is that the self of the test class, or the self as the object of the class under test?
Apologies in advance if I don't understand what you are trying to do, but I think this might work:
import unittest
from unittest.mock import patch
class MyClass:
def __init__(self):
self.x = 0
self.y = 0
def some_method(self):
self.x = 4
self.y = 6
class OtherClass:
def other_method(self):
self.x = 5
self.y = 16
class MyTestClass(unittest.TestCase):
#patch('__main__.MyClass.some_method', new=OtherClass.other_method)
def test_patched(self):
a = MyClass()
a.some_method()
self.assertEqual(a.x, 5)
self.assertEqual(a.y, 16)
def test_not_patched(self):
a = MyClass()
a.some_method()
self.assertEqual(a.x, 4)
self.assertEqual(a.y, 6)
if __name__ == "__main__":
unittest.main()
This replaces some_method() with other_method() when patched, which sets different values for attributes x, y, and when the test is run, it gives the results:
..
----------------------------------------------------------------------
Ran 2 tests in 0.020s
OK
EDIT: to answer question about how to do inside the test function without mocking a class...
def test_inside_patch(self):
def othermethod(self):
self.x = 5
self.y = 16
patcher = patch('__main__.MyClass.some_method', new=othermethod)
patcher.start()
a = MyClass()
a.some_method()
self.assertEqual(a.x, 5)
self.assertEqual(a.y, 16)
patcher.stop()
Make sure you call start() and stop() on the patcher otherwise you can get into a situation where the patch is active and you don't want it to be. Note that to define the mock function inside the test code function, I didn't use patch as a decorator, because the mock function has to be defined before using the 'new' keyword in patch. If you want to use patch as a decorator you have to define the mock function someplace before the patch, defining it inside of MyTestClass also works, but it seems you really want to have the mock function defined inside your test function code.
EDIT: added summary of 4 ways I see to do this...
# first way uses a class outside MyTest class
class OtherClass:
def other_method(self):
...
class MyTest(unittest.TestCase):
#patch('path_to_MyClass.some_method', new=OtherClass.other_method)
def test_1(self)
...
# 2nd way uses class defined inside test class
class MyOtherClass:
def other_method(self):
...
#patch('path_to_MyClass.some_method', new=MyOtherClass.other_method)
def test_2(self):
...
# 3rd way uses function defined inside test class but before patch decorator
def another_method(self):
...
#patch('path_to_MyClass.some_method', new=another_method)
def test_3(self):
...
# 4th way uses function defined inside test function but without a decorator
def test_4(self):
def yet_another_method(self):
...
patcher = patch('path_to_MyClass.some_method', new=yet_another_method)
patcher.start()
...
patcher.stop()
None of these uses a side_effect, but they all solve the problem of mocking a class method and changing some attributes. Which one you choose depends on the application.
Related
I am trying to test a class method which is called within an__init__ function.
class abc:
def __init__(path):
list = []
foo(path)
bar('hello') # test function bar
def foo(self, path):
# does a bunch of stuff and creates internal list
list =
def bar(self):
# does a bunch of stuff and uses list
I would like to write a test for method bar here which I guess must be called through an instance of class abc. I can mock list array for this test, but cannot understand how to avoid the call to foo().
Just mock foo method for the time of testing bar. You can use patch.object.
A full example below:
import unittest
from unittest.mock import patch
class MyClass:
def __init__(self, path):
self.list = []
self.foo(path)
self.bar('/init')
def foo(self, path):
self.list.append(path)
def bar(self, path):
self.list.insert(0, path)
class MyTestClass(unittest.TestCase):
#patch.object(MyClass, 'foo')
def test_bar_decorated(self, mock):
a = MyClass('/foo')
a.bar('/bar')
self.assertEqual(a.list, ['/bar', '/init']) # .foo() wasn't invoked
if __name__ == '__main__':
unittest.main()
Notice that, a mock is created for you and passed in as an extra argument to the decorated function (we don't use it in this test). To avoid that you can use context manager version of patch.object:
def test_bar_context_manager(self):
with patch.object(MyClass, 'foo'):
a = MyClass('/foo')
a.bar('/bar')
self.assertEqual(a.list, ['/bar', '/init']) # same behaviour
I have a class where I have multiple methods. I want to use one of the methods as a decorator for other methods. For this I am using following syntax:
#self.action
def execute(self,req):
where action is other method in my class. But it doesn't work and throws exception as
name 'self' is not defined
You cannot use a method of the class while defining it; there is no self within the class nor is the class 'baked' yet to even access any class.
You can treat methods as functions to use as a decorator:
class SomeClass():
def action(func):
# decorate
return wrapper
#action
def execute(self, req):
# something
If action is defined on a base class, then you'd have to refer to the name via the base class:
class Base():
#staticmethod
def action(func):
# decorate
return wrapper
class Derived(Base):
#Base.action
def execute(self, req):
# something
For Python 2, you'd have to make action a static method here, as otherwise you get an unbound method that'll complain you cannot call it without an instance as the first argument. In Python 3, you can leave off the #staticmethod decorator there, at least for the purposes of the decorator.
But note that action cannot then be used as a method directly; perhaps it should not be part of the class at all at that point. It is not part of the end-user API here, presumably the decorator is not used by consumers of the instances of these classes.
Just beware that both the decorator and the decorated function are unbound methods, so you can only access the self (or cls for classmethods) in the inner scope of the decorator, and must manually bind the decorated method to the instance bound in the inner decorator.
class A:
x = 5
y = 6
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
#decorate
def func(self):
return self.y
A().func() # 30!!
Still trying to wrap my head around how decorators could be inherited and overridden.
Beware that for the decorator to work it can't be bound to an instance. That is: there is no way to make this work
a = A()
#a.decorate
def func(*args):
return 1
Despite this pattern is much more common than the asked here.
At this point the question raises: is it a method at all or just code that you happen to hide in a class?
The only way to prevent the decorator being wrongfully bound is to declare it as a staticmethod, but then it must be in a previous super class because to be used it must be bound to the static class reference which would not be yet defined, just as the self.
class A:
x = 1
#staticmethod
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
class B(A):
#A.decorate
def func(self):
return 1
class C():
x = 2
#B.decorate
def func(self):
return 1
a = A()
class D():
x = 3
#a.decorate
def func(self):
return 1
B().func() # 1
C().func() # 2
D().func() # 3
But as you can see, there is no way for the decorator to use the state of its own class. class A from this last example just happens to be a mixin with a default x variable and an "unrelated" static decorator.
So, again, is it a method?
To overcome all of this, you can bind the staticmethod in your same class to an arbitrary type. Namely, the builtin type will do.
class A:
x = 1
#staticmethod
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
#decorate.__get__(type)
def func(self):
return 1
class B:
x = 2
#A.decorate
def func(self):
return 1
class C:
x = 3
#(A().decorate) # Only for Python 3.9+, see PEP-614
def func(self):
return 1
A().func() # 1
B().func() # 2
C().func() # 3
But this features too much magic for my taste. And still not a method for my gut.
In python "self" is passed to instance methods as an argument (the first), "self" is just a convention is possible to call it "foobarbaz" (of course it would be silly)… the point is that, from the outside "self" is not defined (because its scope is the method)… you can't decorate class methods with other class methods, instead you have to write a separate class!
This code:
class testclass:
def __init__(self,x,y):
self.x = x
self.y = y
self.test()
def test():
print('test')
if __name__ == '__main__':
x = testclass(2,3)
yields:
Error:
TypeError:test() takes no argument(1 given)
I'm calling the test function without any parameter, why does the error say that I have given one?
You call the methods as self.test(). You should mentally translate that to test(self) to find out how the call will be "received" in the function's definition. Your definition of test however is simply def test(), which has no place for the self to go, so you get the error you observed.
Why is this the case? Because Python can only look up attributes when specifically given an object to look in (and looking up attributes includes method calls). So in order for the method to do anything that depends on which object it was invoked on, it needs to receive that object somehow. The mechanism for receiving it is for it to be the first argument.
It is possible to tell Python that test doesn't actually need self at all, using the staticmethod decorator. In that case Python knows the method doesn't need self, so it doesn't try to add it in as the first argument. So either of the following definitions for test will fix your problem:
def test(self):
print('test')
OR:
#staticmethod
def test():
print('test')
Note that this is only to do with methods invoked on objects (which always looks like some_object.some_method(...)). Normal function invocation (looking like function(...)) has nothing "left of the dot", so there is no self, so it won't be automatically passed.
Pass self to your test method:
def test(self):
print('test')
You need to do this because Python explicitly passes a parameter referring to the instantiated object as the first parameter. It shouldn't be omitted, even if there are no arguments to the method (because of the error specified).
Python always passes the instance as the first argument of instance methods, this means that sometimes the error messages concerning the number of arguments seems to be off by one.
class testclass:
def __init__(self,x,y):
self.x = x
self.y = y
self.test()
def test(self): ## instance method
print('test', self)
if __name__ == '__main__':
x = testclass(2,3)
If you don't need access to the class or the instance, you can use a staticmethod as shown below
class testclass:
def __init__(self,x,y):
self.x = x
self.y = y
self.test()
#staticmethod
def test():
print('test')
if __name__ == '__main__':
x = testclass(2,3)
A classmethod is similar, if you need access to the class, but not the instance
class testclass:
def __init__(self,x,y):
self.x = x
self.y = y
self.test()
#classmethod
def test(cls):
print('test', cls)
if __name__ == '__main__':
x = testclass(2,3)
OK,
I know this is mentioned in the manual, and probably has to do with side_effect and/or return_value, but a simple, direct example will help me immensely.
I have:
class ClassToPatch():
def __init__(self, *args):
_do_some_init_stuff()
def some_func():
_do_stuff()
class UUT():
def __init__(self, *args)
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
Now, I want to unit test the UUT class, and mock the ClassToPatch. Knowing the UUT class will instantiate exactly two ClassToPatch objects, I want the Mock framework to return a new Mock object for each instantiation, so I can later assert calls on each separately.
How do I achieve this using the #patch decorator in a test case? Namely, how to fix the following code sample?
class TestCase1(unittest.TestCase):
#patch('classToPatch.ClassToPatch',autospec=True)
def test_1(self,mock1,mock2):
_assert_stuff()
Here's a quick'n'dirty example to get you going:
import mock
import unittest
class ClassToPatch():
def __init__(self, *args):
pass
def some_func(self):
return id(self)
class UUT():
def __init__(self, *args):
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
self.test_property = (resource_1.some_func(), resource_2.some_func())
class TestCase1(unittest.TestCase):
#mock.patch('__main__.ClassToPatch', autospec = True)
def test_1(self, mock1):
ctpMocks = [mock.Mock(), mock.Mock()]
ctpMocks[0].some_func.return_value = "funky"
ctpMocks[1].some_func.return_value = "monkey"
mock1.side_effect = ctpMocks
u = UUT()
self.assertEqual(u.test_property, ("funky", "monkey"))
if __name__ == '__main__':
unittest.main()
I've added test_property to UUT so that the unit test does something useful. Now, without the mock test_property should be a tuple containing the ids of the two ClassToPatch instances. But with the mock it should be the tuple: ("funky", "monkey").
I've used the side_effect property of the mock object so that a different instance of ClassToPatch is returned on each call in the UUT initialiser.
Hope this helps.
Edit: Oh, by the way, when I run the unit test I get:
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Here is another version which is more generic to handle any number of instances created:
class TestUUT:
def test_init(self, mocker):
class MockedClassToPatchMeta(type):
static_instance = mocker.MagicMock(spec=ClassToPatch)
def __getattr__(cls, key):
return MockedClassToPatchMeta.static_instance.__getattr__(key)
class MockedClassToPatch(metaclass=MockedClassToPatchMeta):
original_cls = ClassToPatch
instances = []
def __new__(cls, *args, **kwargs):
MockedClassToPatch.instances.append(
mocker.MagicMock(spec=MockedClassToPatch.original_cls))
MockedClassToPatch.instances[-1].__class__ = MockedClassToPatch
return MockedClassToPatch.instances[-1]
mocker.patch(__name__ + '.ClassToPatch', new=MockedClassToPatch)
UUT()
# since your original code created two instances
assert 2 == len(MockedClassToPatch.instances)
If you need more thorough validation for each instance you can access MockedClassToPatch.instances[0] or MockedClassToPatch.instances[1].
I've also created a helper library to generate the meta class boilerplate for me. To generate the needed code for your example I wrote:
print(PytestMocker(mocked=ClassToPatch, name=__name__).mock_classes().mock_classes_static().generate())
I want to mock MyClass.toBeMockedMethod which is called in MyClass.mymethod using the following script. It runs without actually mocking it. Not sure why....
class Test_mytest(MockerTestCase):
def mock_it_up(self, function, result=None, mmin=0, mmax=None):
function = self.m.mock()
function(ARGS)
self.m.result(result)
self.m.count(mmin, mmax)
def setUp(self):
self.m = Mocker()
self.mock_it_up(MyClass.toBeMockedMethod)
self.o=Myclass(0)
def test_one_atom(self):
self.o.mymethod()
def tearDown(self):
pass
if __name__ == '__main__':
main()
As with this question what you're really trying to do is patch your instance of MyClass. If MyClass is a new-style class then you can do this:
class Test_mytest(MockerTestCase):
def mock_it_up(self, function, result = None, mmin = 0, mmax = None):
methodToMock = getattr(self.p, function)
methodToMock()
self.m.result(result)
self.m.count(mmin, mmax)
def setUp(self):
self.m = Mocker()
self.o = MyClass(0)
self.p = self.m.patch(self.o)
self.mock_it_up('toBeMockedMethod')
# Put more calls to mock_it_up here.
self.m.replay()
def test_one_atom(self):
self.o.mymethod()
This will modify self.o so that calls to toBeMockedMethod are mocked.
However, if MyClass is not a new-style class then patching won't work. In this case, you can use type simulation to trick MyClass into doing what you want. For example:
class Test_mytest(MockerTestCase):
def mock_it_up(self, function, result = None, mmin = 0, mmax = None):
methodToMock = getattr(self.mockObj, function)
methodToMock()
self.m.result(result)
self.m.count(mmin, mmax)
def setUp(self):
self.m = Mocker()
self.o = MyClass(0)
self.mockObj = self.m.mock(MyClass)
self.mock_it_up('toBeMockedMethod')
# Put more calls to mock_it_up here.
self.m.replay()
def test_one_atom(self):
MyClass.mymethod(self.mockObj)
Note that the mocker's mock method is called with the class to be type-simulated. Later, instead of calling self.o.mymethod() we call MyClass.mymethod(...). Now MyClass.mymethod() expects an instance of MyClass as its first argument, but fortunately the mock object is masquerading as an instance of MyClass so the call goes through. When mymethod() calls toBeMockedMethod() it will actually call the mocked method, not the real method.
I quickly hacked up an test MyClass like this:
class MyClass():
def __init__(self, x):
self.x = x
def toBeMockedMethod(self):
print "Not Mocked!"
def mymethod(self):
self.toBeMockedMethod()
and when I ran this code as a unit test I got:
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
which is the desired result.