Python unittest patch mock entire class - python

I have a class that I want to patch in my unittests.
class OriginalClass():
def method_a():
# do something
def method_b():
# do another thing
Now I created another class to patch it with, so the code for patching it is like
class MockClass(OriginalClass):
def method_a():
# This will override the original method and return custom response for testing.
patcher = patch('OriginalClass', new=MockClass)
mock_instance = patcher.start()
This works exactly as I want it to and I can return whatever responses required for my unittests.
Now this issue is when I want to verify that a method is called with the right parameters in the unittests.
I tried
mock_instance.method_a.assert_called_once()
But it fail with error AttributeError: 'function' object has no attribute 'assert_called_once'.
How can I test the method calls here?

AttributeError: 'function' object has no attribute 'assert_called_once'.
Once mock object is created, there is no method_a exists, you have to call once m.method_a() before assert.
m = mock.create_autospec(OriginalClass)
m.method_a()
m.method_a.assert_called_once()
patch mock entire class
I took it as mock the whole class and all its methods, I would take an example from here
https://docs.python.org/3.3/library/unittest.mock-examples.html
Applying the same patch to every test method, Here is my example, patch the entire Primary class as MockPrimay for every methods and every tests, setup or SetupClass could be added for the methods needed, even the whole class is mocked, but not every methods to be used in the tests.
from tests.lib.primary_secondary import Secondary
#mock.patch('tests.lib.primary_secondary.Primary')
class TestSecondaryMockPrimary(unittest.TestCase):
def test_method_d(self, MockPrimary):
MockPrimary().process()
MockPrimary().process.return_value = 1
oc = Secondary()
self.assertEqual(oc.method_d(), 1)
import tests
self.assertIs(tests.lib.primary_secondary.Primary, MockPrimary)
The Primary is needed for the Secondary for this test
class Primary(object):
def __init__(self, param):
self._param = param
def process(self):
if self._param == 1:
self._do_intermediate_process()
self._do_process()
class Secondary(object):
def __init__(self):
self.scl = Primary(1)
def method_d(self):
return self.scl.process

I think wraps can be useful here:
from unittest.mock import patch
class Person:
name = "Bob"
def age(self):
return 35
class Double(Person):
def age(self):
return 5
with patch('__main__.Person', wraps=Double()) as mock:
print(mock.name) # mocks data
print(mock.age()) # runs real methods, but still spies their calls
mock.age.assert_not_called()
Output:
<MagicMock name='Person.name' id='139815250247536'>
5
...
raise AssertionError(msg)
AssertionError: Expected 'age' to not have been called. Called 1 times.
Calls: [call()].

Related

Mock class instances without calling `__init__` and mock their respective attributes

I have a class MyClass with a complex __init__ function.
This class had a method my_method(self) which I would like to test.
my_method only needs attribute my_attribute from the class instance.
Is there a way I can mock class instances without calling __init__ and by setting the attributes of each class instance instead?
What I have:
# my_class.py
from utils import do_something
class MyClass(object):
def __init__(self, *args, **kwargs):
# complicated function which I would like to bypass when initiating a mocked instance class
pass
def my_method(self):
return do_something(self.my_attribute)
What I tried
#mock.patch("my_class.MyClass")
def test_my_method(class_mock, attribute):
instance = class_mock.return_value
instance.my_attribute = attribute
example_instance = my_class.MyClass()
out_my_method = example_instance.my_method()
# then perform some assertions on `out_my_method`
however this still makes usage of __init__ which I hope we can by-pass or mock.
As I mentioned in the comments, one way to test a single method without having to create an instance is:
MyClass.my_method(any_object_with_my_attribute)
The problem with this, as with both options in quamrana's answer, is that we have now expanded the scope of any future change just because of the tests. If a change to my_method requires access to an additional attribute, we now have to change both the implementation and something else (the SuperClass, the MockMyClass, or in this case any_object_with_my_attribute_and_another_one).
Let's have a more concrete example:
import json
class MyClass:
def __init__(self, filename):
with open(filename) as f:
data = json.load(f)
self.foo = data.foo
self.bar = data.bar
self.baz = data.baz
def my_method(self):
return self.foo ** 2
Here any test that requires an instance of MyClass. is painful because of the file access in __init__. A more testable implementation would split apart the detail of how the data is accessed and the initialisation of a valid instance:
class MyClass:
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def my_method(self):
return self.foo ** 2
#classmethod
def from_json(cls, filename):
with open(filename) as f:
data = json.load(f)
return cls(data.foo, data.bar, data.baz)
You have to refactor MyClass("path/to/file") to MyClass.from_json("path/to/file"), but wherever you already have the data (e.g. in your tests) you can use e.g. MyClass(1, 2, 3) to create the instance without requiring a file (you only need to consider the file in the tests of from_json itself). This makes it clearer what the instance actually needs, and allows the introduction of other ways to construct an instance without changing the interface.
There are at least two options I can see:
Extract a super class:
class SuperClass:
def __init__(self, attribute):
self.my_attribute = attribute
def my_method(self):
return do_something(self.my_attribute)
class MyClass(SuperClass):
def __init__(self, *args, **kwargs):
super().__init__(attribute) # I don't know where attribute comes from
# complicated function which I would like to bypass when initiating a mocked instance class
Your tests can instantiate SuperClass and call my_method().
Inherit from MyClass as is and make your own simple __init__():
class MockMyClass(MyClass):
def __init__(self, attribute):
self.my_attribute = attribute
Now your test code can instantiate MockMyClass with the required attribute and call my_method()
For instance, you could write the test as follows
def test_my_method(attribute):
class MockMyClass(MyClass):
def __init__(self, attribute):
self.my_attribute = attribute
out_my_method = MockMyClass(attribute).my_method()
# perform assertions on out_my_method

Python mocking class instantiation and accessing private member variable

I want to test a method inside a class which updates the value of a member variable. However class takes constructor arguments which are not required in the method to test.
class SimpleClass(Database):
count = None
def intoTwo(self, v):
self.count = 2*v
Now I am testing method as follows
import unittest
import mock
class TestSimpleClass(unittest.TestCase):
#mock.patch('SimpleClass', autospec=True)
def test_intoTwo(self, mock_simpleclass):
mock_instance = mock_simpleclass.return_value
mock_instance.intoTwo(2)
self.assertEqual(mock_instance.count,4)
I am getting following error:
<NonCallableMagicMock name='SimpleClass().count' id='139921148836112'>
Please suggest the solution for this. I count not find something relevant in other posts or blogs.

Decorator factory on a unittest method

def register_processor2(processor_name='SomeProcessor'):
def decorator(func):
class SomeProcessor(GenericPaymentProcessor, TriggeredProcessorMixin):
name = processor_name
transaction_class = Transaction
#staticmethod
def setup(data=None):
pass
#wraps(func)
def func_wrapper(*args, **kwargs):
PaymentProcessorManager.register(SomeProcessor)
result = func(*args, **kwargs)
PaymentProcessorManager.unregister(SomeProcessor)
return result
return func_wrapper
return decorator
def register_processor(func):
class SomeProcessor(GenericPaymentProcessor, TriggeredProcessorMixin):
name = 'SomeProcessor'
transaction_class = Transaction
#staticmethod
def setup(data=None):
pass
#wraps(func)
def func_wrapper(*args, **kwargs):
PaymentProcessorManager.register(SomeProcessor)
result = func(*args, **kwargs)
PaymentProcessorManager.unregister(SomeProcessor)
return result
return func_wrapper
class TestPaymentMethodEndpoints(APITestCase):
#register_processor
def test_put_detail_cannot_change_processor(self):
self.assertEqual(True, False)
Ok so the decorator register_processor works as expected. And the test fails, but I want to make the name of the inner class customizable so I went for a decorator factory implementation instead.
The thing is when running the test decorated with register_processor2 I get the following:
AttributeError: 'TestPaymentMethodEndpoints' object has no attribute '__name__'
This is from #wraps(func), my question is why is func here an instance of TestPaymentMethodEndpoints, and not the bound method?
Also if I remove the #wraps decorator then the test runs and passes.
I'd expect that the test would not be discovered as func_wrapper does not start with test_* and even if it is discovered then it should fail.
Any insight on what is happening and how I'd go about doing this?
EDIT
So I figured it out even if the decorator factory has arguments that have default values you still need to place () when calling it.
But would still love to hear an explanation of what happened in case of the tests passing / getting discovered in the first place.
class TestPaymentMethodEndpoints(APITestCase):
#register_processor()
def test_put_detail_cannot_change_processor(self):
self.assertEqual(True, False)
Makes sense now that I think about it :D, gosh you learn something new each day!
I think you're now asking "how come the unittest module can find test cases that have been wrapped in functions with names that don't start test?"
The answer to that is because unittest doesn't use the names of the functions to find the methods to run, it uses the attribute names of the test case classes to find them.
So try running the following code:
from unittest import TestCase
def apply_fixture(func):
def wrap_with_fixture(self):
print('setting up fixture...')
try:
func(self)
finally:
print('tearing down fixture')
return wrap_with_fixture
class MyTestCase(TestCase):
#apply_fixture
def test_something(self):
print('run test')
print('Attributes of MyTestCase: %s' % dir(MyTestCase))
print('test_something method: %s' % MyTestCase.test_something)
mtc = MyTestCase()
mtc.test_something()
You will see that the output from dir contains the name test_something:
Attributes of MyTestCase: ['__call__', ...lots of things..., 'test_something']
but that the value of that attribute is the wrapping function wrap_with_fixture:
test_something method: <function apply_fixture.<locals>.wrap_with_fixture at 0x10d90aea0>
This makes sense when you consider that when you create a function you are both creating a function with the name provided and a local variable with the same name, and that the decorator # syntax is just syntactic sugar, so the following would have been an equally valid albeit longer-winded way of creating your test case class:
class MyTestCase(TestCase):
def test_something(self):
print('run test')
# Overwrite existing 'local' (or 'class' variable in this context)
# with a new value. We haven't deleted the test_something function
# which still exists but now is owned by the function we've created.
test_something = apply_fixture(test_something)

Passing extra argument to TestCase Setup

I am writing tests for my django app using TestCase, and would like to be able to pass arguments to a parent class's setUp method like so:
from django.test import TestCase
class ParentTestCase(TestCase):
def setUp(self, my_param):
super(ParentTestCase, self).setUp()
self.my_param = my_param
def test_something(self):
print('hello world!')
class ChildTestCase(ParentTestCase):
def setUp(self):
super(ChildTestCase, self).setUp(my_param='foobar')
def test_something(self):
super(ChildTestCase, self).test_something()
However, I get the following error:
TypeError: setUp() takes exactly 2 arguments (1 given)
I know that this is because only self is still passed, and that I need to overwrite to class __init__ to get this to work. I am a newbie to Python and not sure how to implement this. Any help is appreciated!
The test runner will call your ParentTestCase.setup with only self as a parameter. Therefore you will add a default value for this case e.g.:
class ParentTestCase(TestCase):
def setUp(self, my_param=None):
if my_param is None:
# Do something different
else:
self.my_param = my_param
Note: be careful not to use mutable values as defaults (see "Least Astonishment" and the Mutable Default Argument for more details).

Are unittest attributes mutable in Python?

Suppose I have this integration test
class TestClass(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.key = '123'
def test_01_create_acc(self):
user = create_account(...)
self.key = user.key
def test_02_check_account(self):
user = check_account(..)
self.assertEqual(self.key, user.key)
It looks like the attribute self.key is not mutable. It stays with the old value from setUpClass. But isn't setUpClass only called once?
The account function creates a key randomly for security reason, so I am not allowed to pass in my secret key. It returns the key, so I need to modify that attribute. Can I?
It looks like each test_ case is isolated.
my_gloabl = None
def setUpClass(cls):
cls.key = my_global
If I change my_global in test1, test2 will get None.
The class is set up only once. But each test method is actually called from a different instance of that test.
You can demonstrate this using the id function, which will return a different number for each object:
import unittest
class TestClass(unittest.TestCase):
#classmethod
def setUpClass(cls):
print "setup"
def test_01_create_acc(self):
print id(self)
def test_02_check_account(self):
print id(self)
unittest.main()
On my computer, this printed:
setup
4300479824
.4300479888
Note how the setup method was called only once, but the id of the instance for test1 and test2 are different.

Categories

Resources