【问题标题】:Python mock class with function and property具有函数和属性的 Python 模拟类
【发布时间】:2016-11-15 10:46:26
【问题描述】:

我在使用函数和属性修补类时遇到了一些问题。

我正在使用的项目结构如下:

project
|- src
|  |- logic
|  |  |- sub_logic
|  |  |  | __init__.py
|  |  |  | cache.py
|  |  |  | manager.py
|  |  | __init__.py  
|- test
|  | test.py  

我的缓存文件是这样的

class Cache(object):
    def __init__(self, val):
        self._val = val

    @property
    def Val(self):
        return self._val

    def other_function(self):
        return False

管理器文件如下所示

from cache import Cache


class Manager(object):
    def __init__(self):
        self._cache = Cache(20)

    def do_something(self):
        if self._cache.Val != 20:
            raise ValueError(u"Val is not 20")

        return True

    def do_something_else(self):
        if self._cache.other_function():
            raise ValueError(u"Something is True")

我尝试进行的测试如下:

from unittest import TestCase
from mock import PropertyMock, patch

from logic.sub_logic.manager import Manager
from logic.sub_logic.cache import Cache


class ManagerTestCase(TestCase):

    def test_01_cache(self):
        manager = Manager()
        self.assertEqual(manager.do_something(), True)

    @patch('logic.sub_logic.manager.Cache.Val', new_callable=PropertyMock)
    def test_02_cache(self, property_mock):
        property_mock.return_value = 20
        manager = Manager()
        self.assertEqual(manager.do_something(), True)

    @patch('logic.sub_logic.manager.Cache', spec=Cache)
    def test_03_cache(self, cache_mock):
        cache_mock.other_function.return_value = True
        manager = Manager()
        with self.assertRaises(ValueError):
            manager.do_something_else()

    @patch('logic.sub_logic.manager.Cache', spec=Cache)
    def test_04_cache(self, cache_mock):
        cache_mock.other_function.return_value = True
        cache_mock.Val = PropertyMock()
        cache_mock.Val.return_value = 20

        manager = Manager()
        with self.assertRaises(ValueError):
            manager.do_something_else()
        self.assertEqual(manager.do_something(), True)

    @patch('logic.sub_logic.manager.Cache.Val', new_callable=PropertyMock)
    @patch('logic.sub_logic.manager.Cache', spec=Cache)
    def test_05_cache(self, cache_mock, property_mock):
        cache_mock.other_function.return_value = True
        property_mock.return_value = 20
        manager = Manager()
        with self.assertRaises(ValueError):
            manager.do_something_else()
        self.assertEqual(manager.do_something(), True)

    @patch('logic.sub_logic.manager.Cache', spec=Cache)
    def test_06_cache(self, cache_mock):
        cache_mock.other_function.return_value = True
        cache_mock.Val = 20

        manager = Manager()
        with self.assertRaises(ValueError):
            manager.do_something_else()
        self.assertEqual(manager.do_something(), True)

问题是 test_04_cache 和 test_05_cache 不工作。 在调试测试时,提供的模拟参数与我预期的一样。但是 Manager 创建了一个 MagicMock,其中属性 Val 不是 PropertyMock 而是 MagicMock。

我在 PyCharm Debugger 中检查了 test_06_cache,它报告了以下内容:

  • cache_mock.Val = {int}20
  • manager._cache.Val = {MagicMock}<MagicMock name='Cache().Val' id='61044848'>

我错过了什么吗?还是不可能?

【问题讨论】:

  • 如果您要模拟缓存,为什么要关心它是否真的作为属性实现?只需设置cache_mock.Val = 20
  • 你建议用cache_mock.Val = 20 替换cache_mock.Val = PropertyMock() cache_mock.Val.return_value = 20?我刚试过,好像不行。
  • 在 test_05 中,您首先模拟 Cache.Val,然后模拟 Cache,它将整个对象替换为 MagicMock。这就是为什么在 test_05 中您将 Val 视为 MagicMock。
  • 你能扩展一下“似乎不起作用”吗?我的观点是,在真正的CacheVal 是一个属性这一事实与Manager 无关,因此当您对后者进行单元测试时,您可以只使用常规属性。
  • 你需要设置cache_mock().Val - 注意你可以访问类,而不是实例。

标签: python python-2.7 unit-testing mocking


【解决方案1】:

当你使用

@patch('logic.sub_logic.manager.Cache', spec=Cache)

生成的模拟是针对的。然后,您的Manager__init__ 中通过调用该类创建一个实例。因此,您应该在mock_cache()(注意括号)上设置属性和返回值,这是将分配给manager._cache 的“实例”,而不是在“类”mock_cache 上。

请注意,由于管理器不知道缓存正在使用@property,您可以设置:

mock_cache().Val = 20

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 2021-07-15
    • 2018-09-21
    • 2018-04-09
    • 2017-04-09
    • 1970-01-01
    • 2013-05-27
    相关资源
    最近更新 更多