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 и т. д.аргумент легко пропустить, но очень важный. Он говорит pytest параметризовать прибор с помощьюпараметры.

Другие вопросы по тегам