在python中使用'autospec = True'时模拟类属性

时间:2012-11-25 09:14:53

标签: python properties attributes mocking subclassing

我希望模拟一个符合以下要求的课程:

  • 该类具有公共读/写属性,在其__init__()方法
  • 中定义
  • 该类具有公共属性,该属性在对象创建时自动递增
  • 我希望使用autospec=True,因此该类的API将严格检查调用

简化的班级样本:

class MyClass():
    id = 0

    def __init__(self, x=0.0, y=1.0):
        self.x = x
        self.y = y
        self.id = MyClass._id
        MyClass.id +=1

    def calc_x_times_y(self):
        return self.x*self.y

    def calc_x_div_y(self, raise_if_y_not_zero=True):
        try:
            return self.x/self.y
        except ZeroDivisionError:
            if raise_if_y_not_zero:
                raise ZeroDivisionError
            else:
                return float('nan')

就属性而言,我需要使mock对象表现为原始对象:

  • 它应该自动递增分配给每个新创建的模拟对象的id
  • 应该允许访问其x,y属性 但模拟方法调用应该被mock拦截,并且其调用签名已经过验证

最好的方法是什么?

修改

我已经尝试过几种方法,包括继承Mock类,使用attach_mock()mock_add_spec(),但总是遇到一些死胡同。

我正在使用标准mock库。

2 个答案:

答案 0 :(得分:2)

由于没有答案,我会发布对我有用的内容(不一定是最好的方法,但是这里有):

我创建了一个模拟工厂,它创建了一个Mock()对象,使用here描述的语法设置其id属性,并返回对象:

 class MyClassMockFactory():
     _id = 0

     def get_mock_object(self, *args,**kwargs):
        mock = Mock(MyClass, autospec = True)
        self._attach_mock_property(mock , 'x', kwargs['x'])
        self._attach_mock_property(mock , 'y', kwargs['y'])
        self._attach_mock_property(mock , 'id', MyClassMockFactory._id)
        MyClassMockFactory._id += 1
        return mock

     def _attach_mock_property(self, mock_object, name, value):
         p = PropertyMock(return_value=value)
         setattr(type(mock_object), name, p)

现在,我可以为我的测试修补MyClass()构造函数:

class TestMyClass(TestCase):
     mock_factory = MyClassMockFactory()

     @patch('MyClass',side_effect=mock_factory.get_mock_object)
     test_my_class(self,*args):
         obj0 = MyClass()
         obj1 = MyClass(1.0,2.2)
         obj0.calc_x_times_y()
         # Assertions
         obj0.calc_x_times_y.assert_called_once_with()
         self.assertEqaul(obj0.id, 0)
         self.assertEqaul(obj1.id, 1)

答案 1 :(得分:0)

很抱歉找到一篇旧帖子,但是让你能够完全按照自己的意愿去做的事情就是修补calc_x_times_ycalc_x_div_y并在那里设置autospec=True,反对嘲笑整个班级的创作。

类似的东西:

@patch('MyClass.calc_x_times_y')
@patch('MyClass.calc_x_div_y')
test_foo(patched_div, patched_times):
my_class = MyClass() #using real class to define attributes
# ...rest of test
相关问题