On Monday, March 12, 2018 at 4:51:53 AM UTC-4, Tim Golden wrote: > I'm contributing to a codebase which makes heavy use of mock in the test > suite, a technique which I'm aware of but have used only rarely. In one > situation it uses mock.mock_open(read_data="...") and then asserts again > mock_open.return_value.read.call_count. > > A code change I've made results in an increase in the call count but > also the open() I've introduced opens the file in binary mode and does > something with the resulting data. > > Hugely simplified, the new code and unchanged test looks like this: > > import os, sys > import unittest > from unittest import mock > > def read_file(filename): > > # > # This section is new > # > with open(filename, "rb") as f: > text = f.read() > if text.startswith(b"#"): > pass > > with open(filename) as f: > text = f.read() > if text.startswith("#"): > pass > > return text > > class TestS(unittest.TestCase): > > def test_read_file(self): > mock_open = mock.mock_open(read_data="abc") > with mock.patch('builtins.open', mock_open): > data = read_file("abc") > assert mock_open.return_value.read.call_count == 1 > > if __name__ == '__main__': > unittest.main() > > > I would expect the test to fail because of the call_count change. But in > fact it errors out because the newly-added "if test.startswith()" > receives a string, not bytes, from the Mock's read_data functionality. > > Ignore for the moment any question of changing the read_file > implementation to assist testing. And leave aside the question of > whether a mock_open is really a good test approach here. > > Is there any way in which I can have the mock_open object return bytes > for the first open and a string for the second? I've looked at setting a > side_effect function against the mock_open.return_value.read Mock, but I > can't see a way of having the function know whether it's supposed to be > returning bytes or string. > > > TJG
2 ways come to mind: 1. Have the side effect function check something that differentiates those 2 calls. In this case, checking mode argument value should work. 2. Rely on the order of calls with a local side effect function closing over a call number. Something like this (untested): def test_read_file(self): call_number = 0 def open_side_effect(filename, mode='r'): call_number += 1 if call_number == 1: return b'some bytes' if call_number == 2: return 'some string' with mock.patch(..., side_effect=open_side_effect): # test Regards, Igor. -- https://mail.python.org/mailman/listinfo/python-list