When trying to unit test a method that returns a tuple and I am trying to see if the code accesses the correct tuple index, python tries to evaluate the expected call and turns it into a string.
call().methodA().__getitem__(0)
ends up getting converted into '().methodA'
in my expected_calls
list for the assertion.
The example code provided, results in the output and traceback:
expected_calls=[call().methodA(), '().methodA']
result_calls=[call().methodA(), call().methodA().__getitem__(0)]
======================================================================
ERROR: test_methodB (badMockCalls.Test_UsingToBeMocked_methods)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\dev\workspace\TestCode\src\badMockCalls.py", line 43, in test_methodB
self.assertListEqual(expected_calls, self.result_calls)
File "C:\Python33\lib\unittest\case.py", line 844, in assertListEqual
self.assertSequenceEqual(list1, list2, msg, seq_type=list)
File "C:\Python33\lib\unittest\case.py", line 764, in assertSequenceEqual
if seq1 == seq2:
File "C:\Python33\lib\unittest\mock.py", line 1927, in __eq__
first, second = other
ValueError: too many values to unpack (expected 2)
----------------------------------------------------------------------
Ran 1 test in 0.006s
FAILED (errors=1)
How do I go about asserting that methodB is calling self.tbm.methodA()[0] properly?
Example code (Python 3.3.2):
import unittest
from unittest.mock import call, patch
import logging
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
_ch = logging.StreamHandler()
_ch.setLevel(logging.DEBUG)
log.addHandler(_ch)
class ToBeMocked(): # external resource that can't be changed
def methodA(self):
return (1,)
class UsingToBeMocked(): # project code
def __init__(self):
self.tbm = ToBeMocked()
def methodB(self):
value = self.tbm.methodA()[0]
return value
class Test_UsingToBeMocked_methods(unittest.TestCase):
def setUp(self):
self.patcher = patch(__name__ + '.ToBeMocked')
self.mock_ToBeMocked = self.patcher.start()
self.utbm = UsingToBeMocked()
# clear out the mock_calls list from the constructor calls
self.mock_ToBeMocked.mock_calls = []
# set result to always point to the mock_calls that we are testing
self.result_calls = self.mock_ToBeMocked.mock_calls
def tearDown(self):
self.patcher.stop()
def test_methodB(self):
self.utbm.methodB()
# make sure the correct sequence of calls is made with correct parameters
expected_calls = [call().methodA(),
call().methodA().__getitem__(0)]
log.debug('expected_calls=' + str(expected_calls))
log.debug(' result_calls=' + str(self.result_calls))
self.assertListEqual(expected_calls, self.result_calls)
if __name__ == "__main__":
unittest.main()
To test for mock_object.account['xxx1'].patch(body={'status': 'active'})
I had to use the test:
mock_object.account.__getitem__.assert_has_calls([
call('xxxx1'),
call().patch(body={'status': 'active'}),
])
I can't explain why this works, this looks like weird behaviour, possibly a bug in mock, but I consistently get these results and it works.
I've just stumbled upon the same problem. I've used the solution/work-around from here:
http://www.voidspace.org.uk/python/mock/examples.html#mocking-a-dictionary-with-magicmock
namely:
>> mock.__getitem__.call_args_list
[call('a'), call('c'), call('d'), call('b'), call('d')]
You can skip the magic function name misinterpretation and check against its arguments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With