Python: Assert mock函数被一个包含另一个字符串的字符串调用



这是我所面临的问题的一个简化版本:假设我有一个函数,它接受一个目录的路径,然后删除它的所有内容,除了(可选地)指定的"keep文件"

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函数,但我不确定如何在这里应用它,或者如果它甚至可能。

是否有任何方法来实现这个用例的包含匹配器?

这不是对您问题的直接回答,但如果由我来编写这些测试,我会采用不同的方法:

  • 创建临时目录
  • 实际删除文件
  • 检查只剩下预期的文件。

这样,您正在测试您想要的实际行为,而不依赖于内部实现细节(即您正在使用os.remove()而不是Pathlib.unlink()之类的替代方案)。

如果您不熟悉,pytest为这种测试提供了一个tmp_pathfixture。但是,填充临时目录仍然有点麻烦,特别是如果您想要测试各种嵌套的文件层次结构。我写了一个名为tmp_files的夹具,使这更容易,虽然,我认为它可能很适合您的问题。下面是测试的样子:

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)

简要地解释一下,tmp_files参数指定在每个临时目录中创建什么文件,并且只是将文件名映射到文件内容的字典。这里所有的文件都是简单的文本文件,但也可以创建符号链接、fifo等。indirect=['tmp_files']参数很容易被忽略,但非常重要。它告诉pytest使用tmp_files参数对tmp_filesfixture进行参数化。

最新更新