用Python模拟目录结构

8

我有一些下面的代码,用于接收文件输入,打开并处理,然后输出一些数据。我已经让功能正常工作了,现在正在进行单元测试,以下是代码示例。

def foo(dir):
    path_to_search = join(dir, "/baz/foo")

   if isdir(path_to_search): 
   #path exists so do stuff...

        for fname in listdir(path_to_search):
            do_stuff()

   else:
       print "path doesn't exist"

我已经能够轻松地创建一个测试,其中过去不存在,但正如您在上面看到的那样,我断言目录结构的“/baz/foo”部分存在(在生产中,目录结构必须具有此文件,在某些情况下,它可能不存在,我们将不需要处理它。)
我尝试使用TempDir和join创建临时目录结构,但代码总是会弹出说路径不存在。
是否可以模拟os.listdir的输出,以便我无需创建遵循所需/baz/foo约定的临时目录结构?

你可以使用Labix Mocker和mocker.replace()来实现这个功能。 - Lukas Graf
@LukasGraf:最好使用标准库unittest.mock或其后备库mock - Martijn Pieters
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Lukas Graf
@LukasGraf:我实际上不喜欢这种魔法。我想要控制哪些代码被修补,而不是让它在任何地方都自动应用。 - Martijn Pieters
@MartijnPieters 在实际编码中,当然是要掌控一切。但在测试中,任何能让我的生活更轻松的东西我都欢迎。至于掌控性:如果一个简单的重构,比如将 import a; a.SomeName 改为 from a import SomeName,会以微妙的方式破坏你的测试,那也是失去了掌控。 - Lukas Graf
1个回答

11
你不需要创建一个虚假的目录结构,你只需要模拟 isdir()listdir() 函数即可。
使用 unittest.mock(或外部mock,对于 Python 版本小于 3.3 的情况,这两个库是完全相同的):
try:
    # Python >= 3.3 
    from unittest import mock
except ImportError:
    # Python < 3.3
    import mock

with mock.patch('yourmodule.isdir') as mocked_isdir, \
        mock.patch('yourmodule.listdir') as mocked_listdir:
    mocked_isdir.return_value = True
    mocked_listdir.return_value = ['filename1', 'filename2']

    yourmodule.foo('/spam/eggs')

    mocked_isdir.assert_called_with('/spam/eggs/baz/foo')
    mocked_listdir.assert_called_with('/spam/eggs/baz/foo')

嗯,你能再解释一下吗?当我输入'spam/eggs'时它好像没起作用。我看我们说isdir应该返回true,list dir应该返回这些文件名,那么这些文件名是“真实的”还是我需要临时创建它们以便读取? - openingceremony
@openingceremony:你的代码并不要求它们是真实的,因为任何需要真实路径的函数都已被你的模拟替换了。它们不需要真实路径,你只需配置它们返回你的测试代码期望看到的内容即可。 - Martijn Pieters
1
@openingceremony:确保您修补了正确的对象!请参阅Where to patch。如果您的模拟未被调用,则表示您没有修补正确的对象。 - Martijn Pieters
1
我仔细检查了一遍,你似乎是对的。我为修补对象提供了完整路径,但我认为问题可能是因为我尝试运行的函数没有listdir作为属性?它从os中导入它,不确定在这种情况下协议是什么,我尝试模拟os.listdir,但似乎也不起作用。 - openingceremony
我想把工作目录改为上一级,所以我将返回值设置为os.listdir('./../'),而不是像你的代码中硬编码的列表。然而,调用os.listdir()返回了模拟对象,而不是调用os.listdir('./../')。我不明白为什么会这样。调用patch(target='os.listdir', new=os.listdir(./../'))解决了问题。不需要使用with语句。 - paperduck
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接