I have a function I want to test. The datahandler
argument is an instance of a class Datahandler
which I have defined elsewhere in the module.
def create_row(row_content, datahandler):
row = {}
# Here comes some code that fills the "row" dictionary based on what was passed in row_content
row = datahandler.process_row(row)
# some more handling happens to the processed row
return row
I tried writing a unit test, but I am having difficulty, because I cannot seem to find a way to replace the process_row
method of a mocked datahandler:
class TestCreateRow(unittest.TestCase):
def fake_process_row(row):
return row
def test_create_row(self):
# create a suitable row_content here
# row_content = ...
expected = {"first column":"first value", "second column":"second value"}
datahandler = Mock()
# !!! This doesn't work - what is the correct solution?
datahandler.process_row = self.fake_process_row
result = create_row(row_content, datahandler)
self.assertDictEqual(expected, result)
I experimented with using return_value
and side_effects
to assign the fake_process_row
function to the mocked datahandler, and even to patch fake_process_row
into Mock
but with any synthactically correct version I tried, the call datahandler.process_row(row)
continued to return a Mock(), when I need it to actually call my fake_process_row
function so it can return a real row.
I have seen some solutions where in the test, a real datahandler
object is created, and fake_process_row
is patched into it. I don't want to do this, because the real class is not entirely trivial to instantiate. It would be much easier to just have a mock with one "working" method, the way I can have a mock with a "working" return value for a property.
CodePudding user response:
So I found out the solution, it was in creating two separate Mocks.
def test_create_row(self):
# create a suitable row_content here
# row_content = ...
expected = {"first column":"first value", "second column":"second value"}
datahandler = Mock()
# The magic happens here
method_mock = Mock(side_effect = self.fake_process_row)
datahandler.process_row = method_mock
result = create_row(row_content, datahandler)
self.assertDictEqual(expected, result)
It was a bit counterintuitive to use side_effect
, but it is what happens as soon as a Mock()
is accessed. So when we set datahandler.process_row
to a Mock with this side effect, it is triggered when the code under test calls the process_row
method, and it is also smart enough to pass the arguments properly.