1

我最近了解了unittest.monkey.patch它的变体,我想用它来单元测试文件读取函数的原子性。但是,该补丁似乎没有任何效果。

这是我的设置。推敲的方法大致是这样的(略):

#local_storage.py

def read(uri):
    with open(path, "rb") as file_handle:
        result = file_handle.read()
    return result

以及执行单元测试的模块(也被删减了):

#test/test_local_storage.py

import unittest.mock
import local_storage

def _read_while_writing(io_handle, size=-1):
    """ The patch function, to replace io.RawIOBase.read. """

    _write_something_to(TestLocalStorage._unsafe_target_file) #Appends "12".
    result = io_handle.read(size) #Should call the actual read.
    _write_something_to(TestLocalStorage._unsafe_target_file) #Appends "34".

class TestLocalStorage(unittest.TestCase):
    _unsafe_target_file = "test.txt"

    def test_read_atomicity(self):
        with open(self._unsafe_target_file, "wb") as unsafe_file_handle:
            unsafe_file_handle.write(b"Test")

        with unittest.mock.patch("io.RawIOBase.read", _read_while_writing): # <--- This doesn't work!
            result = local_storage.read(TestLocalStorage._unsafe_target_file) #The actual test.
            self.assertIn(result, [b"Test", b"Test1234"], "Read is not atomic.")

这样,补丁应该确保每次您尝试读取它时,文件都会在实际读取之前和之后被修改,就好像它同时发生一样,从而测试我们读取的原子性。

单元测试当前成功,但我已经用打印语句验证了补丁函数实际上并没有被调用,所以文件永远不会得到额外的写入(它只是说“测试”)。我还故意将代码修改为非原子的。

所以我的问题是:如何修补readlocal_storage 模块内的 IO 句柄功能?我在其他地方读到人们倾向于替换 open() 函数以返回类似 a 的东西StringIO,但我不明白这如何解决这个问题。

我需要支持 Python 3.4 及更高版本。

4

1 回答 1

1

我自己终于找到了解决方案。

问题是mock不能模拟用 C 编写的对象的任何方法。其中之一就是RawIOBase我遇到的。

所以确实解决方案是模拟open返回一个包装器RawIOBase。我无法mock为我制作一个包装器,所以我自己实现了它。

有一个预定义的文件被认为是“不安全的”。每次对包装器进行任何调用时,包装器都会写入此“不安全”文件。这允许测试文件写入的原子性,因为它在写入时将其他内容写入不安全文件。我的实现通过写入临时(“安全”)文件然后将该文件移动到目标文件上来防止这种情况。

包装器对该函数有一个特殊情况,因为要正确测试原子性,它需要在读取期间read写入文件。所以它首先读取文件的一半,然后停止并写入一些东西,然后继续读取。这个解决方案现在是半硬编码的(半路多远),但我会找到一种方法来改进它。

你可以在这里看到我的解决方案:https ://github.com/Ghostkeeper/Luna/blob/0e88841d19737fb1f4606917f86e3de9b5b9f29b/plugins/storage/localstorage/test/test_local_storage.py

于 2016-08-27T17:36:05.933 回答