对类属性使用side_effect不起作用

时间:2018-08-15 03:42:04

标签: python-3.x unit-testing

我在弄清楚如何使用side_effectMagicMock属性时遇到了麻烦,但是似乎只有在我修补函数时才能使用。但是,我想修补属性。这是一个最小的代码示例:

# in calendar.py
class Weeks():
    a = property(lambda self: 0)
    b = property(lambda self: 0)
    c = property(lambda self: 0)

@patch('calendar.Weeks')
def test_something_else(weeks_mock):

    weeks_mock.return_value.a.side_effect = [0, 10, 20, 30]
    weeks_mock.return_value.b = 40
    weeks_mock.return_value.c = 50
    # some more code...

我也没有像这样尝试使用PropertyMock

@patch('calendar.Weeks')
def test_something_else(weeks_mock):

    type(weeks_mock).a = PropertyMock(side_effect=[0, 10, 20, 30])
    weeks_mock.return_value.b = 40
    weeks_mock.return_value.c = 50
    # some more code...

我将不胜感激!

2 个答案:

答案 0 :(得分:1)

  

使用__get__模拟和测试类属性

获取 说明

被调用以获取所有者类的属性(类属性访问)或该类的实例的属性(实例属性访问)。

with mock.patch.object(Weeks, 'a') as mock_a:
    mock_a.__get__ = mock.Mock(return_value='mocked_cls_attr')
    self.assertEqual(Weeks.a, 'mocked_cls_attr')

不确定为什么会出错,这是一个有效的测试,可帮助您找出错误。

import unittest
import mock


class Weeks():
    a = property(lambda self: 0)
    b = property(lambda self: 0)
    c = property(lambda self: 0)


class TestWeeks(unittest.TestCase):

    @mock.patch.object(Weeks, 'a')
    @mock.patch.object(Weeks, 'b')
    @mock.patch.object(Weeks, 'c')
    def test_something_else(self, mockc, mockb, mocka):
        # the attr a now mocka is becoming a function using side_effect
        mocka.side_effect = [0, 10, 20, 30]
        mockb.__get__ = mock.Mock(return_value=40)
        mockc.__get__ = mock.Mock(return_value=50)

        # mocka() not mocka as it is a function now
        self.assertEqual(mocka(), 0)
        self.assertEqual(mocka(), 10)
        week = Weeks()
        self.assertEqual(week.b, 40)
        self.assertEqual(week.c, 50)
        # week.a with using function will trigger a failure
        # AssertionError: <MagicMock name='a' id='139943677051600'> != 20
        self.assertEqual(week.a(), 20)

    def test_property(self):
        # no need to mock a property object, just replace it
        week = Weeks()
        week.a = property(lambda self: 1)
        self.assertTrue(isinstance(week.a, property))
        self.assertEqual(week.a.fget(0), 1)

运行测试:

nosetests -v python2_unittests/tests/test_cls_attr.py

结果:

test_something_else (python2_unittests.tests.test_cls_attr.TestWeeks) ... ok

答案 1 :(得分:0)

如果将每个属性的new_callable传递给patch,则

PropertyMock将起作用:

class Weeks():
    a = property(lambda self: 0)
    b = property(lambda self: 0)
    c = property(lambda self: 0)

@patch('__main__.Weeks.a', new_callable=PropertyMock)
@patch('__main__.Weeks.b', new_callable=PropertyMock)
@patch('__main__.Weeks.c', new_callable=PropertyMock)
def test_something_else(mock_c, mock_b, mock_a):

    mock_a.side_effect = [0, 10, 20, 30]
    mock_b.return_value = 40
    mock_c.return_value = 50
    # some more code...
    weeks = Weeks()
    assert weeks.a == 0
    assert weeks.a == 10
    assert weeks.b == 40
    assert weeks.c == 50

实际上,您使用PropertyMock的示例适用于a。我只需要修改bc使其完全起作用:

@patch('__main__.Weeks')
def test_something_else(weeks_mock):
    type(weeks_mock).a = PropertyMock(side_effect=[0, 10, 20, 30])
    type(weeks_mock).b = PropertyMock(return_value=40)
    type(weeks_mock).c = PropertyMock(return_value=50)
    # some more code...
    assert weeks_mock.a == 0
    assert weeks_mock.a == 10
    assert weeks_mock.b == 40
    assert weeks_mock.c == 50

如果您需要更复杂的逻辑,请直接修补到新的property

@patch('__main__.Weeks.a', property(lambda self: 40 if self.b else 50))
def test_something_complicated():
    # some more code...
    weeks = Weeks()
    assert weeks.a == 50