47

我有一个定义类属性的基类和一些依赖它的子类,例如

class Base(object):
    assignment = dict(a=1, b=2, c=3)

我想用不同的分配对这个类进行单元测试,例如空字典、单个项目等。这当然非常简化,这不是重构我的类或测试的问题

我最终提出的(pytest)测试是

from .base import Base

def test_empty(self):
    with mock.patch("base.Base.assignment") as a:
        a.__get__ = mock.Mock(return_value={})
        assert len(Base().assignment.values()) == 0

def test_single(self):
    with mock.patch("base.Base.assignment") as a:
        a.__get__ = mock.Mock(return_value={'a':1})
        assert len(Base().assignment.values()) == 1

这感觉相当复杂和 hacky - 我什至不完全理解它为什么工作(虽然我熟悉描述符)。mock 是否会自动将类属性转换为描述符?

感觉更合乎逻辑的解决方案不起作用:

def test_single(self):
    with mock.patch("base.Base") as a:
        a.assignment = mock.PropertyMock(return_value={'a':1})
        assert len(Base().assignment.values()) == 1

要不就

def test_single(self):
    with mock.patch("base.Base") as a:
        a.assignment = {'a':1}
        assert len(Base().assignment.values()) == 1

我尝试过的其他变体也不起作用(作业在测试中保持不变)。

模拟类属性的正确方法是什么?有没有比上述方法更好/更容易理解的方法?

4

5 回答 5

42

base.Base.assignment被简单地替换为一个Mock对象。您通过添加__get__方法使其成为描述符。

这有点冗长,有点不必要;你可以简单地base.Base.assignment直接设置:

def test_empty(self):
    Base.assignment = {}
    assert len(Base().assignment.values()) == 0

当然,这在使用测试并发时不太安全。

要使用 a PropertyMock,我会使用:

with patch('base.Base.assignment', new_callable=PropertyMock) as a:
    a.return_value = {'a': 1}

甚至:

with patch('base.Base.assignment', new_callable=PropertyMock, 
           return_value={'a': 1}):
于 2014-03-11T11:49:08.163 回答
14

也许我错过了一些东西,但如果不使用这可能PropertyMock吗?

with mock.patch.object(Base, 'assignment', {'bucket': 'head'}):
   # do stuff
于 2017-06-13T17:05:13.623 回答
8

为了提高可读性,您可以使用@patch装饰器:

from mock import patch
from unittest import TestCase

from base import Base

class MyTest(TestCase):
    @patch('base.Base.assignment')
    def test_empty(self, mock_assignment):
        # The `mock_assignment` is a MagicMock instance,
        # you can do whatever you want to it.
        mock_assignment.__get__.return_value = {}

        self.assertEqual(len(Base().assignment.values()), 0)
        # ... and so on

您可以在http://www.voidspace.org.uk/python/mock/patch.html#mock.patch找到更多详细信息。

于 2014-03-12T11:35:52.873 回答
5

如果您的类(例如队列)已经在您的测试中导入- 并且您想要修补MAX_RETRY attr - 您可以使用@patch.object或者更好地使用@patch.multiple

from mock import patch, PropertyMock, Mock
from somewhere import Queue

@patch.multiple(Queue, MAX_RETRY=1, some_class_method=Mock)
def test_something(self):
    do_something()


@patch.object(Queue, 'MAX_RETRY', return_value=1, new_callable=PropertyMock)
def test_something(self, _mocked):
    do_something()
于 2017-02-11T19:56:55.630 回答
1

这是一个如何对您的Base课程进行单元测试的示例:

  • 模拟不同类型的多个类属性(即:dictint
  • 使用带有或的@patch装饰器和框架。pytestpython 2.7+3+

# -*- coding: utf-8 -*-
try: #python 3
    from unittest.mock import patch, PropertyMock
except ImportError as e: #python 2
    from mock import patch, PropertyMock 

from base import Base

@patch('base.Base.assign_dict', new_callable=PropertyMock, return_value=dict(a=1, b=2, c=3))
@patch('base.Base.assign_int',  new_callable=PropertyMock, return_value=9765)
def test_type(mock_dict, mock_int):
    """Test if mocked class attributes have correct types"""
    assert isinstance(Base().assign_dict, dict)
    assert isinstance(Base().assign_int , int)
于 2017-05-03T12:34:31.817 回答