如何模拟os.listdir来伪装Python中的文件和目录?

Upe*_*dra 11 python unit-testing mocking

我有一个专有的存储库格式,我正在尝试开发一个Python模块来处理这些存储库.回购格式如下:

/home/X/
       |
       + alpha/
       |
       + beta/
       |
       + project.conf
Run Code Online (Sandbox Code Playgroud)

X是一个项目.alpha并且beta是此项目中的文件夹,它们代表此项目中的.一是一个容器,在这个回购和它所代表的是真的不相关的这个问题.repo X还有根级别的文件; project.conf是这样一个文件的一个例子.

我有一个名为Project抽象项目的类X.的Project类有一个方法load()在于构建一个在内存中的表示.

class Project(object):

    def load(self):
        for entry in os.listdir(self.root):
            path = os.path.join(self.root, entry)
            if os.path.isdir(path):
                group = Group(path)
                self.groups.append(group)
                group.load()
            else:
                # process files ...
Run Code Online (Sandbox Code Playgroud)

load()通过模拟文件系统对方法进行单元测试,我有:

import unittest
from unittest import mock
import Project

class TestRepo(unittest.TestCase):

    def test_load_project(self):
        project = Project("X")

        with mock.patch('os.listdir') as mocked_listdir:
            mocked_listdir.return_value = ['alpha', 'beta', 'project.conf']
            project.load()
            self.assertEqual(len(project.groups), 2)
Run Code Online (Sandbox Code Playgroud)

这确实os.listdir成功模拟了.但我无法欺骗Python将其mocked_listdir.return_value视为由文件和目录组成.

如何嘲笑或者os.listdiros.path.isdir,在相同的测试,使得测试将看到alphabeta为目录和project.conf一个文件?

geo*_*xsh 17

你可以使用pyfakefs,它是一个非常方便的lib来测试在假文件系统上运行.

如果你使用pytest,它有一个插件,所有文件系统函数都已经修补,你只需要使用它的fsfixture:

import os

def test_foo(fs):
    fs.CreateFile('/home/x/alpha/1')
    fs.CreateFile('/home/x/beta/2')
    fs.CreateFile('/home/x/p.conf')    
    assert os.listdir('/home/x/')) == ['alpha', 'beta', 'p.conf']
Run Code Online (Sandbox Code Playgroud)

或者如果您愿意unittest:

import os
import unittest

from pyfakefs import fake_filesystem_unittest

class TestRepo(fake_filesystem_unittest.TestCase):

    def setUp(self):
        self.setUpPyfakefs()

    def test_foo(self):
        os.makedirs('/home/x/alpha')
        os.makedirs('/home/x/beta')
        with open('/home/x/p.conf', 'w') as f:
            f.write('foo')
        self.assertEqual(os.listdir('/home/x/'), ['alpha', 'beta', 'p.conf'])


if __name__ == "__main__":
    unittest.main()
Run Code Online (Sandbox Code Playgroud)


Upe*_*dra 5

我设法通过将iterable传递给模拟对象的side_effect属性来实现所需的行为isdir

import unittest
from unittest import mock
import Project

class TestRepo(unittest.TestCase):

  def test_load_project(self):
      project = Project("X")

      with mock.patch('os.listdir') as mocked_listdir:
        with mock.patch('os.path.isdir') as mocked_isdir:
          mocked_listdir.return_value = ['alpha', 'beta', 'project.conf']
          mocked_isdir.side_effect = [True, True, False]
          project.load()
          self.assertEqual(len(project.groups), 2)
Run Code Online (Sandbox Code Playgroud)

关键是mocked_isdir.side_effect = [True, True, False]线。可迭代的布尔值应匹配传递给该mocked_listdir.return_value属性的目录和文件条目的顺序。