My goal is to patch a class' __new__ method in order to control the exact object it creates in a test case. See the code below.
Each of the two tests test_1 and test_2 work as expected when run individually. When they are both run consecutively, however, test_2 fails with:
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Even if I store away the original __new__ method pointer and restore it at the end of test_1 (something that pytest takes care of anyway) it still produces the same result.
Any idea what is going on here? And what a working approach could be to achieve my goal?
import pytest
# The class:
class A:
def __init__(self, x):
self.x = x
def test_1(mocker):
"""First test patches the new method."""
# Create object:
a = A(x=3)
# Make sure new method always returns this object:
mocker.patch.object(A, "__new__", return_value=a)
# Switch off the initializer (so it doesn't overwrite values in the pre-made object):
mocker.patch.object(A, "__init__", return_value=None)
# Do the test:
res = A(x=1)
assert res.x == 3
def test_2():
"""Second test uses the class as intended."""
A(2) # <- Fails (when run right after test_1)
Update
Thanks to #OctaveL for pointing out that with this class definition, the tests work fine:
class A(object):
def __new__(cls, *_, **__):
return super(A, cls).__new__(cls)
def __init__(self, x):
self.x = x
Related
I have class MyClass in file main.py
class MyClass:
def __init__(self, a=1, b=2, c=3):
self.a = a
self.b = b
self.c = c
self.d = None
self.e = None
self.f = None
def method_1(self):
return self.a + self.b
def method_2(self):
return self.d + 4
def method_3(self):
return self.e + 10
def run(self):
self.d = self.method_1()
self.e = self.method_2()
self.f = self.method_3()
# a lot more stuff here that takes time
and I have the following tests in file test_file.py
import unittest
from unittest.mock import patch
from main import MyClass
class TestMyClass(unittest.TestCase):
def test_method_2_v1(self):
# This test creates a real instance, but the input self.d is created manually in the test,
# in a different way it is done in the production code (in the production code it is done through run())
instance = MyClass()
instance.d = instance.method_1()
instance.e = instance.method_2()
assert instance.e == 7
def test_method_2_v2(self):
# This test creates a real instance, but the input self.d is created manually in the test,
# in a different way it is done in the production code (in the production code it is done through run())
# This case does not run method_1, but gives an explicit value to self.d
instance = MyClass()
instance.d = 3
instance.e = instance.method_2()
assert instance.e == 7
#patch('main.MyClass', spec=True)
def test_method_2_v3(self, my_class_mock):
# This test uses a mock
mock_instance = my_class_mock()
mock_instance.configure_mock(d=3)
assert MyClass.method_2(mock_instance) == 7
I believe the comments and the code clearly explain the differences.
Which one is the best practice and why?
Is there a better solution?
Main difference between the 3 methods
I think that the main difference between the 3 test methods is:
test_method_2_v1() invokes method_1() of MyClass
test_method_2_v2() and test_method_2_v3() don't invoke method_1() of MyClass
This means that test_method_2_v1() executes an indirect test of method_1() while the other 2 test methods execute only a test of method_2().
Comparison between test_method_2_v2() and test_method_2_v3()
Between test_method_2_v2() and test_method_2_v3() I think that the best is test_method_2_v3() because the use of a mock object, when it is possible, is in general preferable because it helps to create unit tests (a mock object is used to simulate the behaviour of an object in a precise test case and not an whole object).
A unit test is a test which verifies a specific functionality. In this case test_method_2_v3() verifies that method_2() returns the value of the attribute d increase of 4.
In my opinion test_method_2_v2() does too many things so it's not a good unit test.
How test_method_2_v2() becomes a unit-test
I think is better to modify test_method_2_v2() and a possible code for it is the following:
def test_method_2_v2(self):
instance = MyClass()
instance.d = 3
# check that method_2 return 'd' increases of 4
assert instance.method_2() == 7
Previous code becomes similar to test_method_2_v3() without using of mock object. Now test_method_2_v2() only verifies the correctness of method_2() (as a test unit must do).
I have a test function which is getting a mocked class via a #mock.patch decorator. Within the function, I want to force a return value of one of the method (which is needed for the test to succeed) as well as make assertions on another of the methods; these methods are exercised by calling a free function in the class's module.
Example module bla.py:
class Tool:
def __init__(self):
self.items = list()
def m1(self, val):
self.items.append(val)
def act(self):
return len(self.items)
def exercise_tool():
toolio = Tool()
for i in range(2):
toolio.m1(i)
return toolio.act() <= 2
and its test:
from unittest import mock
import bla
#mock.patch("bla.Tool")
def test_exercise_tool(mtool):
mtool.act.return_value = 3
assert not bla.exercise_tool() # verify the mocked return value
mtool.m1.assert_was_called()
The test fails with TypeError: '<=' not supported between instances of 'MagicMock' and 'int'.
So how do I get this test to pass?
I find myself with a layout problem. I have an instance method that returns an instance of another class, but to do so requires the first instance. For example:
class bar:
def __init__(self, barvalue, foo_instance, flag, x):
# munge barvalue based on foo_instance, flag and x
self._data = barvalue
class foo(basefoo):
def __init__(self, value, flag, x):
self._data=value # same as basefoo
if flag:
self._extra=x #absent in basefoo
innervalue=value
self.munged = bar(innervalue, self, flag, x)
The self.munged = bar(value, self, x, y) seems a bit hackish, so I would like to check if this the cleanest way.
Nested class tangent
If need be, bar could be made a sub-class [sic. nested class] of foo — the idea of bar(basefoo(value),barvalue, flag, x) is nice, but I could write a class method to covert basefoo to a foo.
But I am not sure what I would gain. In a previous edit I misunderstood how the nested class would work. It turns out the inner class (bar) does not receive the self form self.bar. I thought it was because __new__ gets called before __init__, but this is not the case as __new__ does not receive the self form self.bar. but this is a topic for another question.
class foo(basefoo):
class bar:
def __init__(self, barvalue, foo_instance, flag, x):
# munge barvalue based on foo_instance, flag and x
self._data = barvalue
def __init__(self, value, flag, x):
self._data=value # same as basefoo
if flag:
self._extra=x #absent in basefoo
innervalue=value
self.munged = self.bar(innervalue, self)
#NOT self.munged = self.bar(innervalue)
and force the user to covert a basefoo object to a foo object to get bar if need be? Is this the right line of thought?
Concern
This is the second time I have found myself writing this layout —the other turned messy see GitHub repo of messy code. So I don't want to repeat the messiness.
So, is this the best way or is there a clever trick?
Also, does this situation have a name? My google-fu failed me.
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.
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())