Python unit test #mock.patch set mock property - python

Is there a way to have class patches with mocked properties set in once place? Say I have the following class I want to test.
class ExampleClass
def __init__(self, specific_args):
# Do something with specific args
self.specific_args = specific_args
self.sub_component = SubComponent(...)
def example_function_double_value(self) -> float:
return SomeFunction(self.specific_args, self.sub_component.value)
and it has the following unit test where I patch out SubComponent and set SubComponent.value to return 3.
import unittest
from unittest import mock
def set_patch(patch: mock.Mock):
patch_return_value = mock.Mock()
patch_return_value.value = 3
patch.return_value = patch_return_value
#mock.patch('SubComponent_Path.SubComponent')
class ExampleUnitTest(unittest.TestCase)
def test_case1(self, patch):
set_patch(patch)
specific_args1 = # Generate specific arguments for this test case
test_class = ExampleClass(specfic_args1)
# Some assertion where I expect `subcomponent.value to be 3`
def test_case2(self, patch):
set_patch(patch)
specific_args2 = # Generate specific arguments for this test case
test_class = ExampleClass(specfic_args2)
# Some other assertion where I expect `subcomponent.value to be 3`
Is there a more elegant way to do this without needing to call that helper function at the start of each test?

Setting return_value attribute of the mock is an efficient way to do it so we don't need to call the helper function.
import unittest
from unittest import mock
#mock.patch('SubComponent_Path.SubComponent')
class ExampleUnitTest(unittest.TestCase)
def setUp(self):
self.example_class = ExampleClass()
self.mocked_sub_component = self.example_class.sub_component
self.mocked_sub_component.value.return_value = 3
def test_case1(self, patch):
self.assertEqual(self.example_class.example_function_double_value, 6)
def test_case2(self, patch):
# Some other test case where I expect `subcomponent.value to be 3`

Related

How to unit test a method called within __init__() function?

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

Testing class methods with pytest

In the documentation of pytest various examples for test cases are listed. Most of them show the test of functions. But I’m missing an example of how to test classes and class methods. Let’s say we have the following class in the module cool.py we like to test:
class SuperCool(object):
def action(self, x):
return x * x
How does the according test class in tests/test_cool.py have to look?
class TestSuperCool():
def test_action(self, x):
pass
How can test_action() be used to test action()?
All you need to do to test a class method is instantiate that class, and call the method on that instance:
def test_action(self):
sc = SuperCool()
assert sc.action(1) == 1
Well, one way is to just create your object within the test method and interact with it from there:
def test_action(self, x):
o = SuperCool()
assert o.action(2) == 4
You can apparently use something like the classic setup and teardown style unittest using the methods here: http://doc.pytest.org/en/latest/xunit_setup.html
I'm not 100% sure on how they are used because the documentation for pytest is terrible.
Edit: yeah so apparently if you do something like
class TestSuperCool():
def setup(self):
self.sc = SuperCool()
...
# test using self.sc down here
I would use any fixtures only to create test environment (like database connection) or data parametrization.
If your data is relatively trivial, you can define it inside the testcase:
def test_action_without_fixtures():
sc = SuperCool()
sc.element = 'snow'
sc.melt()
assert sc.element == 'water'
Example with parametrization:
#pytest.mark.parametrize("element, expected", [('snow', 'water'), ('tin', 'solder')])
def test_action_with_parametrization(element, expected):
sc = SuperCool()
sc.element = element
sc.melt()
assert sc.element == expected

DI with unit tests called from elsewhere in application?

I am attempting to do something similar to the following:
import unittest
class foo:
one = 1
two = 1
class bar:
one = 2
two = 2
class my_test(unittest.TestCase):
def __init__(self, di_source):
self.di = di_source
print 'initing my_test'
def setUp(self):
print 'setting up!'
def tearDown(self):
print 'tearing down :('
def test_case_one(self):
self.assertEqual(self.di.one,1)
def test_case_two(self):
self.assertEqual(self.di.two, 2)
di_one = foo()
di_two = bar()
# called from elsewhere in my application
test_one = my_test(di_one).run()
test_one = my_test(di_two).run()
My goal is to:
Be able to call run() on a test suite
Provide a DI container at runtime to that test suite
Take advantage of the setUp and tearDown functionality provided by the unit test framework
However, it seems when I attempt to do this that the unittest framework doesn't like my constructor:
AttributeError: 'my_test' object has no attribute '_testMethodName'
Is there a better way to structure this example to avoid this problem?
How about using something like this:
This allows you to create shared resources for a single suite, pass the resource to all unittests, and then test the object with multiple methods.
'''Example using a shared resource in a unittest'''
import unittest
def callable_function():
'''Generic callable_function, this should actually be connected to an object constructor or something else''
return {'a': 3}
class MyTest(unittest.TestCase):
'''Custom unittest test case'''
def __init__(self, resource, method_name):
super(MyTest, self).__init__(method_name)
self._resource = resource
def test_getitem(self):
'''Test getting item'''
self.assertEquals(self._resource['a'], 3)
def test_setitem(self):
'''Test getting item'''
self._resource['b'] = 2
self.assertEquals(self._resource['b'], 2)
def test_mutable(self):
'''Test changes persist across tests'''
self.assertEquals(self._resource['b'], 2)
def run_suite():
'''Run complete unittest suite'''
suite = unittest.TestSuite()
item = callable_function()
suite.addTests([
MyTest(item, 'test_getitem'),
MyTest(item, 'test_setitem'),
MyTest(item, 'test_mutable'),
])
runner = unittest.TextTestRunner()
runner.run(suite)
if __name__ == '__main__':
run_suite()
EDIT: If you need to discover methods on the fly, you can do the following:
import inspect
def get_tests(cls):
return [k for k, v in cls.__dict__.items() if k.startswith('test') and inspect.ismethod(v)]
for name in get_tests(MyTest):
suite.addTest(MyTest(resource, name))
The idea is simple: override the __init__ method so it takes a resource and method name, bind the resource to the class, and initialize the TestCase as normal.
When you run the test, just use the bound resource.

Patch a method outside python class

I am interested in patching a method which is called by another method in one file. Example - original.py file contains -
def A():
a = 10
b = 5
return a*b;
def B():
c = A()
return c* 10
I want to write unit test for this file , say call it test.py
import mock
import unittest
class TestOriginal(unitest.TestCase):
def test_Original_method(self):
with patch(''):
How can I use patch and mock modules to test original.py. I want A() to always return MagicMock() object instead of an integer.
You simply patch out the A global in the module under test. I'd use the #patch decorator syntax here:
import mock
import unittest
import module_under_test
class TestOriginal(unitest.TestCase):
#patch('module_under_test.A')
def test_Original_method(self, mocked_A):
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)
This passes in the MagicMock mock object for A() as an extra argument to the test method.
Note that we explicitly named the module here. You could also use patch.object(), just naming the attribute on the module (which are your module globals):
class TestOriginal(unitest.TestCase):
#patch.object(module_under_test, 'A')
def test_Original_method(self, mocked_A):
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)
You can still use a with statement too, of course:
class TestOriginal(unitest.TestCase):
def test_Original_method(self):
with patch('module_under_test.A') as mocked_A:
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)

How do you mock patch a python class and get a new Mock object for each instantiation?

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

Categories