如何正确使用unittest.mock.patch在单元测试中使用假对象?

2024-06-24 13:10:54 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在研究使用unittest.mock将对象替换为假的对象(作为一个玩具项目,因为它看起来很有趣)。在documentation的一部分,有一个用io.StringIO实例替换对象的例子,所以我可以看到理论上这应该是可行的,但是我不能让一个特定的玩具示例不需要跳转一些额外的环就可以工作。考虑此代码,保存为^ {CD2>}:

from unittest.mock import patch

class FileSystem:

    def __init__(self, filename="file.txt"):
        self.filename = filename

    def write(self, content):
        with open(self.filename, 'w') as f:
            f.write(content)

    def read(self):
        with open(self.filename) as f:
            return f.read()

class FileSystemFake:

    def __init__(self, filename="file.txt"):
        self.filename = filename
        self.content = None
        self.file_exists = False

    def write(self, content):
        self.content = content
        self.file_exists = True

    def read(self):
        if self.file_exists:
            return self.content
        raise FileNotFoundError

class FileSystemFakeHoldingInstance(FileSystemFake):

    instance = None

    @classmethod
    def __call__(cls, *args, **kwargs):
        if cls.instance is not None:
            return cls.instance
        cls.instance = FileSystemFakeHoldingInstance(*args, **kwargs)
        return cls.instance

def say_hello():
    filesystem = FileSystem("hello.txt")
    filesystem.write("hello")

def test_self_no_patch():
    say_hello()
    with open("hello.txt") as f:
        assert f.read() == "hello"

@patch('main.FileSystem', new_callable=FileSystem)
def test_self(fake):
    say_hello()
    assert fake.read() == "hello"

@patch('main.FileSystem', new_callable=FileSystemFake)
def test_fake(fake):
    say_hello()
    assert fake.read() == "hello"

@patch('main.FileSystem', new_callable=FileSystemFakeHoldingInstance)
def test_fake_holder(fake):
    say_hello()
    assert fake.read() == "hello"

@patch('main.FileSystem', new_callable=FileSystemFakeHoldingInstance)
def test_fake_holder_reference(fake):
    say_hello()
    assert FileSystemFakeHoldingInstance.instance.read() == "hello"

玩具示例提供了一个类FileSystem,该类将文件保存在文件系统上并将其读回。这个类是我为了避免文件系统访问而伪造的。我创建了一个FileSystemFake类,它假装保存一个文件并将其读回;还有一个FileSystemFakeHoldingInstance类,它保存了FileSystemFake的一个实例,并实现了一个返回它的__call__方法

我有一个简单的测试(test_self_no_patch),它使用文件系统进行交互,工作正常。我的第一个困惑点发生在test_self,它试图用对FileSystem(同一类)的调用替换对FileSystem的调用,但抛出“TypeError:'FileSystem'对象不可调用”。我在写了test_fake之后写了这个,它用FileSystemFake替换了FileSystem失败,出现了一个类似的错误:“TypeError:'FileSystemFake'对象不可调用”

第二个问题在test_fake_holder中可见,在这里我们得到一个FileNotFoundError:根据文档,传递到函数中的对象是创建的伪对象。“write”函数可以正常运行,但对假函数调用“read”不会返回已写入的文本

最后,在test_fake_holder_reference中,我从holder上的一个静态实例中读取了文本,这是有效的

为什么我会看到这种特殊的行为(类不可调用,补丁不返回“正确”的对象)?是否有一系列参数可以传递给patch,使其直接与FileSystemFake一起工作


Tags: 对象instancetestselfhelloreaddefcontent