【问题标题】:How to mock a zip file如何模拟一个zip文件
【发布时间】:2019-08-06 21:03:33
【问题描述】:

我想模拟一个ZipFile。特别是,我需要一个模拟

  1. 通过zipfile.is_zipfile() 测试,
  2. 返回zipfile.ZipFile().namelist() 的字符串列表,并且
  3. 仅使用标准库。

我正在测试的代码在给定的 zip 存档(即 .py.zip.whl 文件)中查找潜在的 Python 模块1

# utils.py

import zipfile
from pathlib import Path

def find_modules(archive=None):
    """Find modules within a given zip archive.

    Inputs:
        archive (str/Path): Zip archive

    Returns:
        list (str): List of module names as strings

    """

    possible_ext = ['.py'. '.zip', '.whl']
    modules = []

    if zipfile.is_zipfile(archive): 
        paths = [Path(p) for p in zipfile.ZipFile(archive).namelist()]
        modules = [p.stem for p in paths if p.stem != '__init__' and p.suffix in possible_ext]

    return modules

巫毒解决方案

我拼凑了以下测试:

# test_utils.py

from mypackage import utils
from unittest import mock

class TestFunctions():

    MOCK_LISTING = ['single_file_module.py', 'dummy.txt',
                    'package_namespace.zip', 'wheel_namespace-0.1-py3-none-any.whl']

    @mock.patch('zipfile.ZipFile')
    @mock.patch('zipfile.is_zipfile')
    def test_find_modules_return_value(self, mock_is_zipfile, mock_zipfile):
        mock_is_zipfile.return_value = True
        mock_zipfile.return_value.namelist.return_value = self.MOCK_LISTING
        modules = utils.find_modules('dummy_archive.zip')

        assert len(modules) == 3


def main():
    """Main function used to run tests manually.

    Use PyTest to run tests in bulk.

    """

    tc = TestFunctions()
    tc.test_find_modules_return_value()


if __name__ == '__main__':

    import time

    start_time = time.time()

    main()

    print("\nThe chosen tests have all passed.")
    print("--- %s seconds ---" % (time.time() - start_time))

问题

我发现仅@mock.path('zipfile.ZipFile') 无法满足我的需求;它未能通过zipfile.is_zipfile() 测试。

  • 如果我在模拟 ZipFile 对象,它不应该自动通过 zipfile.is_zipfile() 测试吗?

  • 我发现我无法使用与namelist 相同的方法来覆盖is_zipfile。也就是说,需要额外的@mock.patch('zipfile.is_zipfile')。我的理解是因为一个ZipFile定义了一个上下文,所以第一个return_value覆盖了上下文的__enter__,然后下一个命名空间就是ZipFile方法级别。为什么is_zipfilenamelist 不能使用相同的方法?


# Test doesn't work
# Fails on: assert 0 == 3 
#            + where 0 = len([])
@mock.patch('zipfile.ZipFile')
def test_find_modules_return_value(self, mock_zipfile):
    mock_zipfile.return_value.is_zipfile.return_value = True
    mock_zipfile.return_value.namelist.return_value = self.MOCK_LISTING
    modules = utils.find_modules('dummy_archive.zip')

    assert len(modules) == 3
  • 也许我离基地太远了,有一种更简单的方法来模拟 .zip 存档?

编辑

根据@Don Kirby 的回答,我发现最直观的模式是:

def test_find_modules_return_value(self):
    # Create mock zipfile and override the is_zipfile function
    with mock.patch('mypackage.utils.zipfile') as mock_zipfile:
        mock_zipfile.is_zipfile.return_value = True
        mock_zipfile.namelist.return_value = self.MOCK_LISTING

        # Since a ZipFile is a separate object, which returns a zipfile (note 
        # that that's lowercase), we need to mock the ZipFile and have it return 
        # the zipfile mock previously created.
        with mock_patch('mypackage.utils.zipfile.ZipFile') as mock_ZipFile:
            mock_ZipFile.return_value = mock_zipfile

            modules = utils.find_modules("/dummy/path/to/check.zip")

    assert len(modules) == 3


1 假设.zip 文件可能包含模块,并且.zip.whl 将在不同的进程中处理。在这一步中,我们只关心文件名。

【问题讨论】:

  • 我其实很喜欢你的巫毒解决方案:) 如果你想稍后重构,你也可以使用更灵活的with mock.patch ... 表单。

标签: python unit-testing mocking zip


【解决方案1】:

您必须单独修补 is_zipfile()ZipFile,因为 is_zipfile() 是一个函数,而不是 ZipFile 类的方法。我想你也许可以通过修补mypackage.utils.zipfile 来修补整个zipfile 模块,但这似乎更令人困惑。

zipfile source code 可能有用。

【讨论】:

    猜你喜欢
    • 2021-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多