Like many people, I'm having issues with mock patching and getting the path right. Specifically, my code references another class in the same file and I'm having trouble patching that reference.
I have the following python file, package/engine/dataflows/flow.py:
class Flow:
def run(self, type):
if type == 'A':
method1()
elif type == 'B':
method2()
else:
backfill = Backfill()
backfill.run()
class Backfill(Flow):
def run(self):
...
And a test file package/tests/engine/dataflows/test_Flow.py
import unittest
from unittest.mock import Mock, patch
from engine.dataflows.flow import Flow
class MockFlow(Flow):
...
class TestFlowRun(unittest.TestCase):
def setUp(self):
self.flow = MockFlow()
def test_run_type_c(self):
with patch('engine.dataflows.flow.Backfill') as mock_backfill:
self.flow.run(type='C')
assert mock_backfill.run.call_count == 1
The patch works in that it doesn't throw an error when run with pytest, but the assertion is failing. I assume that is because the local reference to the Backfill class has essentially already been imported when MockFlow was initialized, but I have been unable to come up with a patching path that handles this.
The contents of flow.py include the Flow base class and a couple of child classes that implement different data flow patterns. They're co-located in the same file for ease of understanding and common dependencies.
The problem is that you are checking the run() function of a class, not an instantiation of that class. The mocked Backfill class will return an instance of the class via its constructor/init. That is the mock / object you will want to check.
with patch('engine.dataflows.flow.Backfill') as mock_backfill:
mocked_backfill_instance = mock_backfill.return_value
self.flow.run(type='C')
assert mocked_backfill_instance.run.call_count == 1
Related
I am trying to mock the super class of a class with a setup similar to this:
File parent.py
class Parent:
def write(self):
*some code*
File child.py
class Child(Parent):
def write(self):
*more code*
super().write()
File mock_parent.py
class MockParent(Parent):
def write(self):
...
My goal would be to replace Parent with MockParent to improve testing of Child, by eliminating real hardware resources.
So far I tried to use mock patch with no success. I tried to patch imports, bases and super but none of these attempts had been successful. I could replace the internals of the Child object, but I would prefer to have a cleaner solution through patching potentially.
The biggest challenge is that the call to the method write of the parent class (by super().write()) is inside the subclass method, otherwise I could simply assign it the function I want to be called.
In this moment I have found this solution which needs a change to the code of the method write of the class Child. This modification must be used only during the test, while, in production code you have to used your production code.
Below I show you the file test_code.py which contains the production code and the test code:
import unittest
from unittest.mock import Mock
class Parent:
def write(self):
print("parent.write()")
class Child(Parent):
def write(self, *super_class):
print("child.write()")
# ----> Here I have changed your code
if len(super_class) > 0:
super_class[0].write()
else:
# ----> ... here you find your production code
super().write()
class MockParent(Parent):
def write(self):
print("mock_parent.write()")
class MyTestCase(unittest.TestCase):
def test_with_parent_mock(self):
print("Execution of test_with_parent_mock")
mock_parent = Mock(wraps = MockParent())
child = Child()
child.write(mock_parent)
mock_parent.write.assert_called_once()
def test_with_parent(self):
print("Execution of test_with_parent")
child = Child()
child.write()
if __name__ == '__main__':
unittest.main()
If you execute this code by the command python test_code.py, you will obtain the following output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Execution of test_with_parent
child.write()
parent.write()
Execution of test_with_parent_mock
child.write()
mock_parent.write()
The output of the test method test_with_parent_mock() shows that you can substitute the write() method of the super class with an other method defined in MockParent.
Instead in the method test_with_parent() you can call the write() method of the class Child normally.
Note about TDD
What I have proposed to you is only a workaround, but I don't know if it is suited to reach your goal because I see that between the tags selected by you, there is TDD. However I hope this code could be useful for you.
I'm trying to unit test a class which is derived from a base_class in an external module. In my dev/test environment I have not access to this external module, which means I have to somehow mock this base_class.
My test-code resides in a different file from the code I'm trying to test.
The problem can be summarized as follows:
my_class.py
import external_module
class MyClass(external_module.ExternalClass):
def test_method(self):
return "successful"
test_my_class.py
import sys
import unittest
from unittest.mock import MagicMock
sys.modules['external_module'] = MagicMock()
from my_class import MyClass
class TestMyClass(unittest.TestCase):
def test_first(self):
my_class = MyClass()
result = my_class.test_method()
self.assertEqual(result, "successful")
if __name__ == '__main__':
unittest.main()
Results
When running test_my_class.py the result are the following.
AssertionError: <MagicMock name='mock.ExternalClass.test_method()' id='140272215184664'> != 'successful'
Clearly since the external_module is mocked, even MyClass becomes an instance of a mock-object.
Similar posts
The problem is similar to as described in Python mock: mocking base class for inheritance, but has the difference that the base_class is from an external module.
Even How to mock a base class with python mock library show som similarities to my problem, though the solutions can not be directly applied.
Tries and failures
To get the import
import external_module
to work in my_class.py
sys.modules['external_module'] = MagicMock()
need to be set in test_my_class.py.
Though, this leads to that external_module.* becomes a Mock-instance.
You could create a helper module mocked_external_module, which can be imported from your tests and also contains a class base_class. Then you do the following in your test code:
import mocked_external_module
sys.modules['external_module'] = mocked_external_module
Plus, every method of your base_class that you need to mock you can create as a Mock or MagicMock.
I'm writing some testing utility library and may want to do the following
def assertSomething(test_case, ...):
test_case.assertEqual()
Is there a way to skip passing test_case around? e.g.
def assertSomething(...):
get_current_unittest_case().assertEqual()
AssertionError
If you just want to do some checks and fail with custom message,
raising AssertionError (via raise or assert) is the way to
go. By default, TestCase.failureException
is AssertionError, and
fail
(which internally is used by convenience methods of unittest.TestCase)
just raises it.
test_things.py:
import unittest
def check_is_zero(number):
assert number == 0, "{!r} is not 0".format(number)
def check_is_one(number):
if number != 1:
raise AssertionError("{!r} is not 1".format(number))
class NumbersTest(unittest.TestCase):
def test_one(self):
check_is_one(1)
def test_zero(self):
check_is_zero(0)
TestCase mixin
An easy and relatively readable way to add new assertions is to
make a “mixin class” that test cases will subclass. Sometimes it
is good enough.
testutils.py that contains mixin:
class ArithmeticsMixin:
def check_is_one(self, number):
self.assertEqual(number, 1)
test_thing.py, actual tests:
import unittest
import testutils
class NumbersTest(unittest.TestCase, testutils.ArithmeticsMixin):
def test_one(self):
self.check_is_one(1)
If there will be many mixin classes, use of base one may help:
import unittest
import testutils
class BaseTestCase(unittest.TestCase, testutils.ArithmeticsMixin):
"""Test case with additional methods for testing arithmetics."""
class NumbersTest(BaseTestCase):
def test_one(self):
self.check_is_one(1)
Thread local and TestCase subclass
Less readable is use of special base class,
thread local
(like global, but is aware of threads) and getter function.
testutils.py:
import unittest
import threading
_case_data = threading.local() # thread local
class ImproperlyUsed(Exception):
"""Raised if get_case is called without cooperation of BaseTestCase."""
def get_case(): # getter function
case = getattr(_case_data, "case", None)
if case is None:
raise ImproperlyUsed
return case
class BaseTestCase(unittest.TestCase): # base class
def run(self, *args, **kwargs):
_case_data.case = self
super().run(*args, **kwargs)
_case_data.case = None
def check_is_one(number):
case = get_case()
get_case().assertEqual(number, 1)
When test case is running, self (test case instance) is saved as
_case_data.case, so later inside check_is_one (or any other function
that is called from inside of test method and wants to access self)
get_case will be able to get reference to test case instance. Note
that after running _case_data.case is set to None, and if
get_case is called after that, ImproperlyUsed will be raised.
test_thing.py:
import testutils
def check_is_zero(number): # example of out-of-testutils function
testutils.get_case().assertEqual(number, 0)
class NumbersTest(testutils.BaseTestCase):
def test_one(self):
testutils.check_is_one(1)
def test_zero(self):
check_is_zero(0)
s̻̙ỵ̞̜͉͎̩s.̠͚̱̹̦̩͓_̢̬ge̡̯̳̖t̞͚̖̳f̜̪̩̪r̙̖͚̖̼͉̹a͏ṃ̡̹e̞̝̱̠̙
Finally, sys._getframe.
Let’s just h́o̞̓̐p̆̇̊̇e you don’t need it, because it is part of CPython,
not Python.
testutils.py:
import sys
import unittest
class ImproperlyUsed(Exception):
"""Raised if get_case can't get "self" TestCase instance."""
def get_case():
case = sys._getframe().f_back.f_back.f_locals.get("self")
if not isinstance(case, unittest.TestCase):
raise ImproperlyUsed
return case
def check_is_one(number):
case = get_case()
get_case().assertEqual(number, 1)
sys._getframe returns frame from the top of call stack, then few
frames below f_locals is taken, and there is self: instance of
unittest.TestCase. Like in previous implementation option, there is
sanity check, but here it is done with isinstance.
test_things.py:
import unittest
import testutils
def check_is_zero(number): # example of out-of-testutils function
testutils.get_case().assertEqual(number, 0)
class NumbersTest(unittest.TestCase):
def test_one(self):
testutils.check_is_one(1)
def test_zero(self):
check_is_zero(0)
If you just want to provide assertEqual for some new type,
take a look at addTypeEqualityFunc.
Im trying to patch multiple methods in a class. Here is my simplified set up
Hook.py is defined as
class Hook():
def get_key(self):
return "Key"
def get_value(self):
return "Value"
HookTransfer.py defined as
from Hook import Hook
class HookTransfer():
def execute(self):
self.hook = Hook()
key = self.hook.get_key()
value = self.hook.get_value()
print(key)
print(value)
I want to mock the methods get_key and get_value in the Hook class. The following works i.e. prints New_Key and New_Value
from HookTransfer import HookTransfer
import unittest
from unittest import mock
class TestMock(unittest.TestCase):
#mock.patch('HookTransfer.Hook.get_key', return_value="New_Key")
#mock.patch('HookTransfer.Hook.get_value', return_value="New_Value")
def test_execute1(self, mock_get_key, mock_get_value):
HookTransfer().execute()
if __name__ == '__main__':
unittest.main()
However this does not. It prints <MagicMock name='Hook().get_key()' id='4317706896'> and <MagicMock name='Hook().get_value()' id='4317826128'>
from HookTransfer import HookTransfer
import unittest
from unittest import mock
class TestMock(unittest.TestCase):
#mock.patch('HookTransfer.Hook', spec=True)
def test_execute2(self, mock_hook):
mock_hook.get_key = mock.Mock(return_value="New_Key")
mock_hook.get_value = mock.Mock(return_value="New_Value")
HookTransfer().execute()
if __name__ == '__main__':
unittest.main()
Intuitively it seems like the second one should work too but it doesnt. Could you help explain why it does not. I suspect it has something to do with "where to patch" but Im unable to get clarity.
You can patch multiple methods of a module or a class using patch.multiple(). Something like this should work for your case:
import unittest
from unittest.mock import MagicMock, patch
class TestMock(unittest.TestCase):
#patch.multiple('HookTransfer.Hook',
get_key=MagicMock(return_value='New_Key'),
get_value=MagicMock(return_value='New_Value'))
def test_execute1(self, **mocks):
HookTransfer().execute()
When patch.multiple() is used as a decorator, the mocks are passed into the decorated function by keyword, and a dictionary is returned when it's used as a context manager.
What you need to is:
mock the class Hook,
from HookTransfer import HookTransfer
from Hook import Hook
import unittest
try:
import mock
except ImportError:
from unittest import mock
class TestMock(unittest.TestCase):
#mock.patch.object(Hook, 'get_key', return_value="New_Key")
#mock.patch.object(Hook, 'get_value', return_value="New_Value")
def test_execute1(self, mock_get_value, mock_get_key):
HookTransfer().execute()
if __name__ == "__main__":
unittest.main()
After some testing I was able to find the issue.
In the second test case, the patch decorator creates a new instance of a Mock class and passes it via mock_hook argument to test_execute2 function. Lets refer to this as mock1. mock1 replaces the Hook class in HookTransfer.py. When self.hook = Hook() is run, it translates to calling __init__ of mock1. By design this returns yet another Mock instance - lets refer to this as mock2. So self.hook points to mock2. But mock_hook.get_key = mock.Mock(return_value="New_Key"), mocks the methods in mock1.
In order to mock correctly, mock2 needs to be patched. This can be done in 2 ways
By mocking the return_value of mock1 (which returns mock2) mock_hook.return_value.get_key = mock.Mock(return_value="New_Key")
Mocking the return value of constructor of mock1 (which returns mock2) mock_hook().get_key = mock.Mock(return_value="New_Key")
Under the wraps both options really do the same thing.
I'm trying to run a TestCase in python 3.3.2 that has several test methods in it:
class ttt(unittest.TestCase):
def setUp(self):
...
def tearDown(self):
...
def test_test1(self):
...
def test_test2(self):
...
if __name__ == "__main__":
instance = ttt()
instance.run()
The documentation states the following:
Each instance of TestCase will run a single base method: the method
named methodName. However, the standard implementation of the default
methodName, runTest(), will run every method starting with test as an
individual test, and count successes and failures accordingly.
Therefore, in most uses of TestCase, you will neither change the
methodName nor reimplement the default runTest() method.
However, when I run the code I get the following:
'ttt' object has no attribute 'runTest'
I want to ask: Is this a bug? And if it's not why is there no runTest method? Am I doing something wrong?
When the unit test framework runs test cases, it creates an instance of the test class for each test.
I.e. to simulate what the unit test framework does you need to do:
if __name__ == "__main__":
for testname in ["test_test1", "test_test2"]:
instance = ttt(testname)
instance.run()
The correct way to run unit tests in a module is:
if __name__ == "__main__":
unittest.main()
... but I assume you know this already.
Regarding runTest: unittest.TestCase.__init__ signature and docstring is:
def __init__(self, methodName='runTest'):
"""Create an instance of the class that will use the named test
method when executed. Raises a ValueError if the instance does
not have a method with the specified name.
"""
Meaning that if you don't specify a test name in the constructor, the default is runTest.