【发布时间】:2019-08-06 21:03:33
【问题描述】:
我想模拟一个ZipFile。特别是,我需要一个模拟
- 通过
zipfile.is_zipfile()测试, - 返回
zipfile.ZipFile().namelist()的字符串列表,并且 - 仅使用标准库。
我正在测试的代码在给定的 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_zipfile和namelist不能使用相同的方法?
# 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