Reputation: 41030
After reading this : How do I mock an open used in a with statement (using the Mock framework in Python)?
I'm able to mock the open function in python using :
with patch(open_name, create=True) as mock_open:
mock_open.return_value = MagicMock(spec=file)
m_file = mock_open.return_value.__enter__.return_value
m_file.read.return_value = 'text1'
diffman = Diffman()
diffman.diff(path1, path2)
It works well when my tested method used one open statement. Here is my tested method :
def diff(self, a, b):
with open(a, 'r') as old:
with open(b, 'r') as new:
oldtext = old.read()
newtext = new.read()
The values of oldtext and newtext are the same ('text1' here).
I would like to have 'text1' for the oldtext and 'text2' for the newtext.
How can I do this ?
Upvotes: 7
Views: 5517
Reputation: 458
Just found another solution:
open_mock1 = mock.mock_open(read_data="file1 data")
open_mock2 = mock.mock_open(read_data="file2 data")
def open_mock_side(file_name, *args, **kwargs):
# can be refactored to use a dict
if file_name.endswith("folder/file1.txt"):
return open_mock1()
if file_name.endswith("folder/file2.txt"):
return open_mock2()
open_mock = mock.mock_open()
open_mock.side_effect = open_mock_side
open_mock.return_value = None
with mock.patch('package.of.function_to_test.open', open_mock):
function_to_test(...)
Upvotes: 3
Reputation: 3542
Perhaps a good possible solution is just to write the code in a way that better lends itself to easily testing it. In the case of 'diff', it seems easy enough (not having much other context, admittedly) to have diff take as arguments already-opened file objects. This is likely a fairly small change to make in the code, and makes testing very easy, since you can then easily provide mock file objects to diff() when you test it, instead of trying to jump through hoops mocking two instances of the same builtin function as a context manager called within... itself... or something ;-)
import StringIO
diff(a, b):
oldtext = a.read()
newtext = b.read()
def test_diff():
a = StringIO.StringIO('text1')
b = StringIO.StringIO('text2')
res = diff(a, b)
<some assertion here>
Would that work for your case?
Upvotes: 3
Reputation: 5776
Here's a quick way of getting what you want. It cheats a little bit because the two file objects in the method under test are the same object and we're just changing the return value of the read call after each read. You can use the same technique in multiple layers if you want the file objects to be different, but it will be quite messy and it may disguise the intent of the test unnecessarily.
Replace this line:
m_file.read.return_value = 'text1'
with:
reads = ['text1', 'text2'] m_file.read.side_effect = lambda: reads.pop(0)
Upvotes: 6