我有一个数据访问类,用于从数据库中获取数据。它是作为一个mixin实现的,即ReferenceData我希望用MockReferenceData类交换这个类,所以在单元测试中我不必访问DB。但是没有用。
*注意:我不能使用依赖注入。因为团队不想使用它,因为它将是非常大的代码更改。所以我希望在运行时替换mixin。
class MockReferenceData(object):
def dbName(self):
return 'mock'
def totalNumberOfSeats(self):
return '10'
class ReferenceData(object):
def dbName(self):
return 'real DB'
def totalNumberOfSeats(self):
return 'Fetch from DB'
class Car(ReferenceData):
def showNumberOfSeats(self):
print self.totalNumberOfSeats()
class Train(ReferenceData):
def showNumberOfSeats(self):
print self.totalNumberOfSeats()
c = Car()
c.showNumberOfSeats()
t = Train()
t.showNumberOfSeats()
def extend_instance(obj, cls):
"""Apply mixins to a class instance after creation"""
base_cls = obj.__class__
base_cls_name = obj.__class__.__name__
obj.__class__ = type(base_cls_name, (base_cls, cls),{})
extend_instance(c, MockReferenceData)
c.showNumberOfSeats() // output now should be 10
输出是:
Fetch from DB
Fetch from DB
Fetch from DB
我希望因为我使用 extend_instance 方法指向新的模拟类输出将是:
Fetch from DB
Fetch from DB
10
答案 0 :(得分:1)
如果您查看班级Car
的{{3}},可以看到:
>>> Car.__mro__
(<class '__main__.Car'>, <class '__main__.ReferenceData'>, <type 'object'>)
首先在Car
上查找方法,然后在ReferenceData
上查找方法,最后在object
上查找。
将此与新课程的MRO进行比较(为清晰起见,我使用NewCar
作为名称):
>>> type('NewCar', (Car, MockReferenceData), {}).__mro__
(<class '__main__.NewCar'>, <class '__main__.Car'>, <class '__main__.ReferenceData'>, <class '__main__.MockReferenceData'>, <type 'object'>)
这包含Car
类的MRO。此处的方法仍会先在Car
上查找,然后在ReferenceData
上查找,以便在totalNumberOfSeats
上找到ReferenceData
时,不会使用来自MockReferenceData
的实施。< / p>
您可以做的是在 Car
类之前将模拟类插入MRO :
>>> type('NewCar', (MockReferenceData, Car), {}).__mro__
(<class '__main__.NewCar'>, <class '__main__.MockReferenceData'>, <class '__main__.Car'>, <class '__main__.ReferenceData'>, <type 'object'>)
现在首先在MockReferenceData
类中查找方法,如果它们不存在,则会回退到之前的版本。所以这个extend_instance
方法适用于这个简单的情况:
def extend_instance(obj, cls):
"""Apply mixins to a class instance after creation"""
obj.__class__ = type(obj.__class__.__name__, (cls, obj.__class__),{})
答案 1 :(得分:1)
谢谢@mata我也做了同样的改变了订单而且工作正常。
def extend_instance(obj, cls):
"""Apply mixins to a class instance after creation"""
base_cls = obj.__class__
base_cls_name = obj.__class__.__name__
obj.__class__ = type(base_cls_name, (cls, base_cls),{})
答案 2 :(得分:0)
不要使用继承。在许多情况下,这是一个过度使用的抽象,这是一个例子。通过使用依赖注入,您可以轻松默认为实际的数据库,但用模拟的后端替换您的后端。
class MockReferenceData(object):
def dbName(self):
return 'mock'
def totalNumberOfSeats(self):
return '10'
class ReferenceData(object):
def dbName(self):
return 'real DB'
def totalNumberOfSeats(self):
return 'Fetch from DB'
class Car(object):
def __init__(self, datasource_class=ReferenceData):
self._datasource = datasource_class() # instead of creation here, you could also pass in an
def showNumberOfSeats(self):
print(self._datasource.totalNumberOfSeats())
real_car = Car()
real_car.showNumberOfSeats()
mocked_car = Car(datasource_class=MockReferenceData)
mocked_car.showNumberOfSeats()
另外请关注PEP8以获取Python中的编码约定。您使用“错误”缩进深度为2,导致像我这样的开发人员编辑问题。方法名也一样。