Python Mock - Mocking several open
Asked Answered
O

3

7

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 ?

Ouellette answered 19/2, 2012 at 12:26 Comment(0)
E
6

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)
Espinal answered 19/2, 2012 at 16:51 Comment(0)
T
3

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?

Tineid answered 19/2, 2012 at 18:53 Comment(0)
S
3

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(...)
Sampler answered 5/7, 2023 at 15:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.