Python: функция Assert mock была вызвана со строкой, содержащей другую строку
Вот упрощенная версия проблемы, с которой я сталкиваюсь: допустим, у меня есть функция, которая принимает путь к каталогу, а затем удаляет все его содержимое, кроме (необязательно) назначенного «сохранить файл»,
import os
KEEP_FILE_CONSTANT = '.gitkeep'
def clear_directory(directory: str, retain: bool = True) -> bool:
try:
filelist = list(os.listdir(directory))
for f in filelist:
filename = os.path.basename(f)
if retain and filename == KEEP_FILE_CONSTANT:
continue
os.remove(os.path.join(directory, f))
return True
except OSError as e:
print(e)
return False
Я пытаюсь написать модульный тест для этой функции, который проверяетos.remove
назывался. В настоящее время я тестирую это:
import pytest
from unittest.mock import ANY
@pytest.mark.parametrize('directory', [
('random_directory_1'),
('random_directory_2'),
# ...
])
@patch('module.os.remove')
def test_clear_directory(delete_function, directory):
clear_directory(directory)
delete_function.assert_called()
delete_function.assert_called_with(ANY)
В идеале, то, что я хотел бы утверждать в тесте, этоdelete_function
был вызван с аргументом, содержащимdirectory
, то есть что-то вроде,
delete_function.assert_called_with(CONTAINS(directory))
или что-то в этом роде. Я смотрел на PyHamcrest , в частности на функцию contains_string , но я не уверен, как ее применить здесь и возможно ли это вообще.
Есть ли способ реализовать сопоставитель CONTAINS для этого варианта использования?
1 ответ
Это не прямой ответ на ваш вопрос, но если бы я писал эти тесты, я бы выбрал другой подход:
- Создайте временный каталог.
- Собственно удалить файлы.
- Убедитесь, что остались только ожидаемые файлы.
Таким образом, вы тестируете фактическое поведение, которое хотите, и не зависите от деталей внутренней реализации (т.е. того факта, что вы используете
Если вы не знакомы, pytest предоставляетtmp_path
приспособление именно для такого рода испытаний. Тем не менее, заполнение временного каталога по-прежнему утомительно, особенно если вы хотите протестировать различные иерархии вложенных файлов. Я написал приспособление под названиемчтобы сделать это проще, и я думаю, что это может подойти для вашей проблемы. Вот как будет выглядеть тест:
import pytest
# I included tests for nested files, even though the sample function you
# provided doesn't support them, just to show how to do it.
@pytest.mark.parametrize(
'tmp_files, to_remove, to_keep', [
pytest.param(
{'file.txt': ''},
['file.txt'],
[],
id='remove-1-file',
),
pytest.param(
{'file-1.txt': '', 'file-2.txt': ''},
['file-1.txt', 'file-2.txt'],
[],
id='remove-2-files',
),
pytest.param(
{'file.txt': '', 'dir/file.txt': ''},
['file.txt', 'dir/file.txt'],
[],
id='remove-nested-files',
marks=pytest.mark.xfail,
),
pytest.param(
{'.gitkeep': ''},
[],
['.gitkeep'],
id='keep-1-file',
),
pytest.param(
{'.gitkeep': '', 'dir/.gitkeep': ''},
[],
['.gitkeep', 'dir/.gitkeep'],
id='keep-nested-files',
marks=pytest.mark.xfail,
),
],
indirect=['tmp_files'],
)
def test_clear_directory(tmp_files, to_remove, to_keep):
clear_directory(tmp_files)
for p in to_remove:
assert not os.path.exists(tmp_files / p)
for p in to_keep:
assert os.path.exists(tmp_files / p)
Чтобы кратко объяснить, параметры указывают, какие файлы создавать в каждом временном каталоге, и представляют собой просто словари, отображающие имена файлов в содержимое файлов. Здесь все файлы являются простыми текстовыми файлами, но также можно создавать символические ссылки, FIFO и т. д.