我为Python改编了一个取自《The Magic Tricks of testing》的例子。桑迪·梅斯著。假设我有这个:
class Gear:
def __init__(self, wheel=Wheel()):
self.wheel = wheel
def get_gear_inches(self):
self._get_ratio() * self.wheel.get_diameter()
现在我想为get_gear_inches()
方法编写单元测试。在"The Magic Tricks of testing"中,Sandi Metz建议不要对wheel.get_diameter()
的返回值(这是一个输出查询)设置期望,因为这会将我们绑定到实现中,而我们只关心返回的结果。所以我们只需要测试返回值,像这样:
def test_get_gear_inches():
gear = Gear()
assert gear.get_gear_inches() == 12 # For example
但是我有点困惑。
- 这不是一个集成测试吗?在我看来,我们正在测试两个组件(
Gear
和Wheel
)的行为,而不仅仅是一个。 - 另外,如果这个传出查询(
self.wheel.get_diameter()
)到达db并且需要很长时间怎么办?
什么是"正确的"?测试方法是什么?
注意:我的替代方案是设置期望并嘲笑Wheel.get_diameter()
的行为,像这样:
def test_get_gear_inches():
wheel_mock = Mock()
wheel_mock.get_diameter.return_value = 123 # We avoid calling the actual implementation
gear = Gear(wheel_mock)
assert Gear().get_gear_inches() == 12 # For example
但在视频中,据我所知,这是不鼓励的。
她说检查gear.wheel.diameter
是反模式的,因为如果weel
的实现发生了变化,那么测试就会被破坏。但是,初始化weel
已经使测试变得更加脆弱,这并没有困扰她。但是,如果可能的话,更好的解决方案是将gear
与wheel
一起测试。或者用更大的东西一起测试它们。但是,如果这些对象具有复杂的行为,并且您希望单独测试它们,那么正确的解决方案是模拟wheel
。否则,当没有这样的需要时,您将测试wheel
和Gear
,因为您在其他地方测试了' wheel。所有的原则都应该只适用于特定的情况,而不应该在违背常识的情况下应用。那些教书的人并不总是提到它。