【问题标题】:Unit testing an outgoing query message对传出查询消息进行单元测试
【发布时间】: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

但我有点困惑。

  1. 这不是集成测试吗?在我看来,我们正在测试两个组件(GearWheel)的行为,而不仅仅是一个。
  2. 另外,如果这个传出查询 (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 是不同的对象。
  • 大概你会在你的实现中使用经过良好测试的类型。您的单元测试对于Wheeltypeobject 一样多。换句话说,显示的第一种方法是正确的:get_gear_inches 需要为给定的输入返回 12,无论它如何达到该数字。您的“改进”测试不尊重实施可能会改变的事实。
  • 另外,我最大的问题是有一个名为get_gear_inches 的方法。一种更 Pythonic 的方法是拥有一个名为 diameter_inches 左右的属性(在 Wheel 中还有一个名为 diameter 的属性,而不是方法 get_diameter
  • 最重要的是要务实。如果您需要大量模拟、模拟和记录,以便在实现发生更改时,您会了解回归测试失败的原因。

标签: python unit-testing testing


【解决方案1】:

她说检查gear.wheel.diameter 是反模式,因为这样如果weel 的实现发生了变化,测试就会被破坏。但是初始化weel 已经使测试变得更加脆弱,这并不困扰她。 但如果可能的话,更好的解决方案是测试gearwheel。或者甚至用更大的东西一起测试它们。 但是如果这些对象具有复杂的行为并且您想单独测试它们,那么正确的解决方案是模拟wheel。否则,当没有这种需要时,你测试wheelGear,因为你在其他地方测试了`wheel。 所有的原则都应该只适用于特定的情况,不应该在违背常识的情况下适用。教书的人并不总是提到它。

【讨论】:

  • 感谢您的回答。我仍然觉得我们在某种程度上将单元测试和集成测试混为一谈。我的意思是,如果您同时测试 WheelGear,您正在测试的“单元”的边界是什么?
  • unittest 表示只测试一个单元。您应该为每种情况决定一个单元是什么,它可能是功能、类或由多个类组成的整个服务。 “单位”并不意味着您不能使用数据库。例如在 Django 框架 (docs.djangoproject.com/en/3.2/topics/testing/overview) 中,单元测试几乎总是使用数据库,这并不妨碍它们被称为单元测试,因为数据库被认为是代码的继承部分
猜你喜欢
  • 2011-02-23
  • 2011-05-15
  • 1970-01-01
  • 2014-01-14
  • 2020-04-13
  • 2016-03-29
  • 2018-07-06
  • 2016-05-30
  • 1970-01-01
相关资源
最近更新 更多