简体   繁体   English

Python:断言模拟 function 被调用了一个包含另一个字符串的字符串

[英]Python: Assert mock function was called with a string containing another string

Here is a simplified version of the problem I am facing: Let's say I have a function that accepts a path to a directory and then removes all of its content except (optionally) a designated "keep file",这是我面临的问题的简化版本:假设我有一个 function 接受目录路径,然后删除其所有内容,除了(可选)指定的“保留文件”,

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

I am trying to write a unit test for this function that verifies the os.remove was called.我正在尝试为此 function 编写一个单元测试,以验证调用了os.remove This is currently how I am testing it:这是目前我正在测试它的方式:

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)

Ideally, what I would like to assert in the test is the delete_function was called with an argument containing directory , ie something like,理想情况下,我想在测试中断言的是delete_function是使用包含directory的参数调用的,例如,

delete_function.assert_called_with(CONTAINS(directory)) 

or something of that nature.或类似的东西。 I have been looking at PyHamcrest , specifically the contains_string function, but I am not sure how to apply it here or if it's even possible.我一直在查看PyHamcrest ,特别是contains_string function,但我不确定如何在此处应用它,或者是否可能。

Is there any way to implement a CONTAINS matcher for this use case?有没有办法为这个用例实现一个 CONTAINS 匹配器?

This isn't a direct answer to your question, but if I were writing these tests, I would take a different approach:这不是您问题的直接答案,但如果我正在编写这些测试,我会采取不同的方法:

  • Make a temporary directory.创建一个临时目录。
  • Actually delete the files.实际删除文件。
  • Check that only the expected files remain.检查是否只保留预期的文件。

This way, you're testing the actual behavior you want, and not depending on internal implementation details (ie the fact that you're using os.remove() instead of some alternative like Pathlib.unlink() ).这样,您正在测试您想要的实际行为,而不是依赖于内部实现细节(即您使用os.remove()而不是Pathlib.unlink() () 之类的替代方法)。

If you're not familiar, pytest provides a tmp_path fixture for exactly this kind of test.如果您不熟悉,pytest 为此类测试提供了一个tmp_path夹具。 However, it's still a bit of a chore to fill in the temporary directory, especially if you want to test a variety of nested file hierarchies.但是,填充临时目录仍然有点麻烦,尤其是如果您想测试各种嵌套文件层次结构。 I wrote a fixture calledtmp_files to make this easier, though, and I think it might be a good fit for your problem.不过,我编写了一个名为tmp_files的夹具来简化此操作,我认为它可能非常适合您的问题。 Here's how the test would look:以下是测试的外观:

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)

To briefly explain, the tmp_files parameters specify what files to create in each temporary directory, and are simply dictionaries mapping file names to file contents.简单解释一下, tmp_files参数指定要在每个临时目录中创建哪些文件,并且只是将文件名映射到文件内容的字典。 Here all the files are simple text files, but it's also possible to create symlinks, FIFOs, etc. The indirect=['tmp_files'] argument is easy to miss but very important.这里所有的文件都是简单的文本文件,但也可以创建符号链接、FIFO 等indirect=['tmp_files']参数很容易忽略但非常重要。 It tells pytest to parametrize the tmp_files fixture with the tmp_files parameters.它告诉 pytest 使用tmp_files参数对tmp_files夹具进行参数化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM