如何模拟类的函数的返回值?

时间:2016-07-24 20:00:58

标签: python unit-testing

我在Python中有一个看起来像这样的方法(在comicfile.py中):

from zipfile import ZipFile

...

class ComicFile():
    ...

    def page_count(self):
        """Return the number of pages in the file."""
        if self.file == None:
            raise ComicFile.FileNoneError()

        if not os.path.isfile(self.file):
            raise ComicFile.FileNotFoundError()

        with ZipFile(self.file) as zip:
            members = zip.namelist()
            pruned = self.prune_dirs(members)
            length = len(pruned)
            return length

我正在尝试为此编写一个单元测试(我已经测试过prune_dirs),所以这就是我所拥有的(test_comicfile.py):

import unittest
import unittest.mock

import comicfile

...

class TestPageCount(unittest.TestCase):

    def setUp(self):
        self.comic_file = comicfile.ComicFile()

    @unittest.mock.patch('comicfile.ZipFile')
    def test_page_count(self, mock_zip_file):
        # Store as tuples to use as dictionary keys.
        members_dict = {('dir/', 'dir/file1', 'dir/file2'):2,
                        ('file1.jpg', 'file2.jpg', 'file3.jpg'):3
        }

        # Make the file point to something to prevent FileNoneError.
        self.comic_file.file = __file__

        for file_tuple, count in members_dict.items():
            mock_zip_file.return_value.namelist = list(file_tuple)
            self.assertEqual(count, self.comic_file.page_count())

当我运行此测试时,我得到以下内容:

F..ss....
======================================================================
FAIL: test_page_count (test_comicfile.TestPageCount)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py", line 1157, in patched
    return func(*args, **keywargs)
  File "/Users/chuck/Dropbox/Projects/chiv/chiv.cbstar/test_comicfile.py", line 86, in test_page_count
    self.assertEqual(count, self.comic_file.page_count())
AssertionError: 2 != 0

----------------------------------------------------------------------
Ran 9 tests in 0.010s

FAILED (failures=1, skipped=2)

好的,self.comic_file.page_count()正在返回0。我尝试将members = zip.namelist()放在page_count之后放置以下行。

print('\nmembers -> ' + str(members))

在测试期间,我得到了这个:

members -> <MagicMock name='ZipFile().__enter__().namelist()' id='4483358280'>

我对单元测试很陌生并且在使用unittest.mock时非常模糊,但我的理解是mock_zip-file.return_value.namelist = list(file_tuple)应该使namelist方法ZipFile方法file_tuple 1}}类依次返回每个namelist内容。 做什么我不知道。

我认为我在这里要做的很清楚,但我似乎无法弄清楚如何覆盖ZipFile方法,以便我的单元测试只测试这一个函数而不是必须也可以处理preg_

1 个答案:

答案 0 :(得分:2)

ZipFile被实例化为context manager。至mock,您必须引用其__enter__方法。

mock_zip_file.return_value.__enter__.return_value.namelist.return_value = list(file_tuple)

您尝试做的事情非常明确,但上下文管理器会增加模拟的复杂性。

一个技巧是当一个模拟注册所有调用它时,在这个例子中它说它有一个调用:

members -> <MagicMock name='ZipFile().__enter__().namelist()' id='4483358280'>

这可以指导您注册模拟对象,将所有()替换为return_value