【发布时间】:2021-10-25 08:34:00
【问题描述】:
我已经为 Python 改编了一个取自 "The Magic Tricks of Testing" by Sandi Metz 的示例。假设我有这个:
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()) 到达数据库并且需要很长时间怎么办?
什么是“正确”的测试方法?
注意:我的替代方案是设定期望并模拟 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()也测试object.__init__、type.__call__等吗? -
@MadPhysicist 那么你会测试什么,如何测试?另外:
Gear在这里仍然是“被测对象”,而Wheel是不同的对象。 -
大概你会在你的实现中使用经过良好测试的类型。您的单元测试对于
Wheel和type和object一样多。换句话说,显示的第一种方法是正确的:get_gear_inches需要为给定的输入返回 12,无论它如何达到该数字。您的“改进”测试不尊重实施可能会改变的事实。 -
另外,我最大的问题是有一个名为
get_gear_inches的方法。一种更 Pythonic 的方法是拥有一个名为diameter_inches左右的属性(在Wheel中还有一个名为diameter的属性,而不是方法get_diameter) -
最重要的是要务实。如果您需要大量模拟、模拟和记录,以便在实现发生更改时,您会了解回归测试失败的原因。
标签: python unit-testing testing