pytest-mock pathlib.Path.open
Мне нужно издеваться pathlib.Path.open
с помощью pytest-mock
.
Реальность open_func
открывает yaml-file
. Возвращаемое значение - обычноеdict
. Как я могу издеватьсяPath.open
просто загрузить другой yaml-file
называется test-config.yaml
?
Мой код не работает должным образом, поскольку conf
просто станет str
("test_config.yaml"). Это должно бытьdict
.
from pathlib import Path
import yaml
def open_func():
with Path.open(Path("./config.yaml")) as f:
return yaml.load(f, Loader=yaml.FullLoader)
def test_open_func(mocker):
mocker.patch("pathlib.Path.open", mocker.mock_open(read_data="test_config.yaml"))
conf = open_func()
assert isinstance(conf, dict)
РЕДАКТИРОВАТЬ: Чтобы приблизиться к моей реальной проблеме, я предоставляю следующий код. У меня есть классTryToMock
, который обычно принимает в качестве входных данных два файла. Методload_files
просто загружает эти файлы (которые на самом деле являются файлами.yaml) и возвращает результат. Эти файлы.yaml на самом деле представляют собой файлы конфигурации.
В своих модульных тестах я буду звонить TryToMock
много раз через pytest parametrize
. Поэтому я хотел бы загрузить исходные файлы конфигурации черезfixture
. Тогда я смогуmonkeypatch
несколько записей в моих различных тестах перед запуском load_files
.
Чтобы больше не загружать исходные файлы, мне нужно издеваться над Path.open
функционировать в TryToMock
. Я хотел бы передатьmonkeypatched
yaml вместо этого (т.е. в виде dict
). Сложность в том, что я должен различать два файла. То есть я не могу просто издеваться надPath.open
работать с тем же содержимым файла.
# TryToMock.py
from pathlib import Path
import yaml
# In my current working folder, I have to .yaml files containing the following
# content for illustrative purpose:
#
# file1.yaml = {'name': 'test1', 'file_type': 'yaml'}
# file2.yaml = {'schema': 'test2', 'currencies': ['EUR', 'USD', 'JPY']}
class TryToMock:
def __init__(self, file_to_mock_1, file_to_mock_2):
self._file_to_mock_1 = file_to_mock_1
self._file_to_mock_2 = file_to_mock_2
def load_files(self):
with Path.open(self._file_to_mock_1) as f:
file1 = yaml.load(f, Loader=yaml.FullLoader)
with Path.open(self._file_to_mock_2) as f:
file2 = yaml.load(f, Loader=yaml.FullLoader)
return file1, file2
# test_TryToMock.py
import os
from pathlib import Path
import pytest
import yaml
from tests import TryToMock
def yaml_files_for_test(yaml_content):
names = {"file1.yaml": file1_content, "file2.yaml": file2_content}
return os.path.join("./", names[os.path.basename(yaml_content)])
@pytest.fixture(scope="module")
def file1_content():
with Path.open(Path("./file1.yaml")) as f:
return yaml.load(f, Loader=yaml.FullLoader)
@pytest.fixture(scope="module")
def file2_content():
with Path.open(Path("./file2.yaml")) as f:
return yaml.load(f, Loader=yaml.FullLoader)
def test_try_to_mock(file1_content, file2_content, monkeypatch, mocker):
file_1 = Path("./file1.yaml")
file_2 = Path("./file2.yaml")
m = TryToMock.TryToMock(file_to_mock_1=file_1, file_to_mock_2=file_2)
# Change some items
monkeypatch.setitem(file1_content, "file_type", "json")
# Mocking - How does it work when I would like to use mock_open???
# How should the lambda function look like?
mocker.patch(
"pathlib.Path.open",
lambda x: mocker.mock_open(read_data=yaml_files_for_test(x)),
)
files = m.load_files()
assert files[0]["file_type"] == "json"
1 ответ
Вы должны предоставить фактическое содержимое файла в read_data
аргумент mock_open
. Вы можете просто создать данные в своем тесте:
test_yaml = """
foo:
bar:
- VAR: "MyVar"
"""
def test_open_func(mocker):
mocker.patch("pathlib.Path.open", mocker.mock_open(read_data=test_yaml))
conf = open_func()
assert conf == {'foo': {'bar': [{'VAR': 'MyVar'}]}}
Или вы можете прочитать данные из своего тестового файла:
def test_open_func(mocker):
with open("my_fixture_path/test.yaml") as f:
contents = f.read()
mocker.patch("pathlib.Path.open", mocker.mock_open(read_data=contents))
conf = open_func()
assert isinstance(conf, dict)
Последний случай также можно переписать, чтобы заменить path
аргумент в open
вызов по тестовой дорожке:
def test_open_func(mocker):
mocker.patch("pathlib.Path.open", lambda path: open("test.yaml"))
conf = open_func()
assert isinstance(conf, dict)
или, если у вас разные тестовые файлы для разных конфигураций, что-то вроде:
def yaml_path_for_test(yaml_path):
names = {
"config.yaml": "test.yaml",
...
}
return os.path.join(my_fixture_path, names[os.path.basename(yaml_path)])
def test_open_func3(mocker):
mocker.patch("pathlib.Path.open", lambda path: open(yaml_path_for_test(path)))
conf = open_func()
assert isinstance(conf, dict)
Вероятно, это то, чего вы хотели достичь в своем тестовом коде.
ОБНОВЛЕНИЕ: это связано со второй частью вопроса (после редактирования). Если у вас есть фикстуры с областью модуля, которые предварительно загружают файлы фикстур, как в вопросе, вы можете сделать что-то вроде этого:
def test_open_func(mocker, file1_content, file2_content):
def yaml_files_for_test(path):
contents = {"file1.yaml": file1_content,
"file2.yaml": file2_content}
data = contents[os.path.basename(path)]
mock = mocker.mock_open(read_data=yaml.dump(data))
return mock.return_value
mocker.patch("pathlib.Path.open", yaml_files_for_test)
conf = open_func()
assert isinstance(conf, dict)
или, если вы предпочитаете не использовать вложенные функции:
def yaml_files_for_test(path, mocker, content1, content2):
contents = {"file1.yaml": content1,
"file2.yaml": content2}
data = contents[os.path.basename(path)]
mock = mocker.mock_open(read_data=yaml.dump(data))
return mock.return_value
def test_open_func5(mocker, file1_content, file2_content):
mocker.patch("pathlib.Path.open",
lambda path: yaml_files_for_test(path, mocker,
file2_content, file2_content))
conf = open_func()
assert isinstance(conf, dict)