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

2022-01-08 00:00:00 python mocking unit-testing

问题描述

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

I have a proprietary repository format and I'm trying to develop a Python module to process these repositories. Repo format goes as:

/home/X/
       |
       + alpha/
       |
       + beta/
       |
       + project.conf

这里,X 是一个项目.alphabeta 是本项目中的文件夹,它们代表本项目中的组.group 是这个 repo 中的一个容器,它所代表的内容实际上与这个问题无关.repo X 在其根级别也有文件;project.conf 就是此类文件的一个示例.

Here, X is a project. alpha and beta are folders inside this project and they represent groups in this project. A group is a container in this repo and what it represents is really not relevant for this question. The repo X also has files in its root level; project.conf is an example of such a file.

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

I have a class called Project that abstracts projects such as X. The Project class has a method load() that builds an in-memory representation.

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 ...

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

To unit test the load() method by mocking the file system, I have:

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)

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

This does mock os.listdir successfully. But I can't trick Python to treat mocked_listdir.return_value as consisting of files and directories.

我如何模拟 os.listdiros.path.isdir,在同一个测试中,这样测试就会看到alphabeta 作为目录,project.conf 作为文件?

How do I mock either os.listdir or os.path.isdir, in the same test, such that the test will see alpha and beta as directories and project.conf as a file?


解决方案

我设法通过将一个可迭代对象传递给模拟的 isdirside_effect 属性来实现所需的行为对象.

I managed to achieve the desired behavior by passing an iterable to the side_effect attribute of the mocked isdir object.

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)

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

The key is the mocked_isdir.side_effect = [True, True, False] line. The boolean values in the iterable should match the order of directory and file entries passed to the mocked_listdir.return_value attribute.

相关文章