So I know that in my unit test I can mock a context manager open(), i.e.:
JavaScript
x
2
1
with open('file_path', 'r') as stats:
2
mocked with
JavaScript
1
2
1
with mock.patch('builtins.open', mock.mock_open(read_data=mock_json)):
2
but is there a way for me to only mock it for a specific file path? Or maybe some other way to ensure that the context manager gets called with the correct path in a unit test?
Advertisement
Answer
To mock open
only for a specific path, you have to provide your own mock object that handles open
differently, depending on the path. Assuming we have some function:
JavaScript
1
4
1
def do_open(path):
2
with open(path, "r") as f:
3
return f.read()
4
where open
shall be mocked to return a file with the content “bar” if path
is “foo”, but otherwise just work as usual, you could do something like this:
JavaScript
1
17
17
1
from unittest import mock
2
from my_module.do_open import do_open
3
4
builtin_open = open # save the unpatched version
5
6
def mock_open(*args, **kwargs):
7
if args[0] == "foo":
8
# mocked open for path "foo"
9
return mock.mock_open(read_data="bar")(*args, **kwargs)
10
# unpatched version for every other path
11
return builtin_open(*args, **kwargs)
12
13
@mock.patch("builtins.open", mock_open)
14
def test_open():
15
assert do_open("foo") == "bar"
16
assert do_open(__file__) != "bar"
17
If you don’t want to save the original open
in a global variable, you could also wrap that into a class:
JavaScript
1
12
12
1
class MockOpen:
2
builtin_open = open
3
4
def open(self, *args, **kwargs):
5
if args[0] == "foo":
6
return mock.mock_open(read_data="bar")(*args, **kwargs)
7
return self.builtin_open(*args, **kwargs)
8
9
@mock.patch("builtins.open", MockOpen().open)
10
def test_open():
11
12