模拟 unlink() 时检查 Path-object 的值

Check the value of Path-object when mocking unlink()

我的生产代码采用一个 pathlib.Path 实例并从中创建第二个具有修改后缀的实例,如果第二个文件存在则将其删除。

def productive_code(fp):
    # "foo" + ".wrong" + ".csv" = "foo.wrong.csv"
    fp.with_suffix('.wrong{}'.format(fp.suffix))
    
    fp_wrong.unlink(missing_ok=True)  # >= Python3.8

在单元测试中,我修补了 pathlib.Path.unlink() 并且我可以检查它是否以及使用哪些参数被调用。

但是如何检查路径对象本身的“值”?我想测试“第二个”文件名是否正确创建。

import unittest
from unittest import mock
import pathlib

    class TestMy(unittest.TestCase):
        @mock.patch('pathlib.Path.unlink')
        def test_unlink(self, mock_unlink):
            productive_code(pathlib.Path('foo.csv'))
            mock_unlink.assert_called_once_with(missing_ok=True)
            # test "foo.wrong.csv" !!!

我试图定义一个 side_effect 并模拟 Path 对象本身(使用 wraps)但无济于事。

但我发现 并且工作正常!
可以用autospec=True

import unittest
from unittest import mock
import pathlib


def productive_code(fp):
    # "foo" + ".wrong" + ".csv" = "foo.wrong.csv"
    fp_wrong = fp.with_suffix('.wrong').with_suffix(fp.suffix)

    fp_wrong.unlink(missing_ok=True)  # >= Python3.8


class TestMy(unittest.TestCase):
    @mock.patch('pathlib.Path.unlink', autospec=True)
    #                                  ^^^^^^^^^^^^^
    def test_unlink(self, mock_unlink):
        productive_code(pathlib.Path('foo.csv'))
        mock_unlink.assert_called_once_with(pathlib.Path("foo.wrong.csv"), missing_ok=True)

此信息不在 mock library documentation but from the "getting started" page 中,它没有任何链接,而是在“下一节”按钮中。我很吃惊我现在才发现它!

回到主题,现在您可以检查哪个路径被 unlinked,您会注意到您的 productive_code 实现是错误的:您假设调用 with_suffix 两次会连接两者,但事实并非如此,根据 the doc :

Return a new path with the suffix changed.

本质上:

>>> from pathlib import Path
>>> Path("a.txt").with_suffix(".mp3")
PosixPath('a.mp3')
>>> Path("a.txt").with_suffix(".mp3").with_suffix(".html")
PosixPath('a.html')
>>> Path("a.txt.mp3.html").with_suffix(".zip")
PosixPath('a.txt.mp3.zip')

测试问题的另一种方法是 this code review's answer :

Mocking system calls, especially those with side effects (mkdir()), is difficult and error prone. [...] If you really need to mock the filesystem part, I would suggest using an existing library such as pyfakefs, although I have never had such a need.

如果您的测试代码出现严重错误,您可能会损坏您的系统(取消链接不应该的文件)。

PS:不错的最小可重现示例 :)