【问题标题】:testing objects using DAO使用 DAO 测试对象
【发布时间】:2009-08-12 18:59:23
【问题描述】:

继续另一个关于测试的类似问题 (see here)。我将使用一个类似的示例(伪代码)

class LinkDisplayer
    method constructor(LinkStorage)
    method displayLatestLinksByCategory(number_of_them)

class LinkStorage
    method saveLink(Link)
    method retrieveLatestLinksByCategory(category, number_of_them)

class Link
    method getUrl()
    method getDescription()
    method getCategory()

所以 linkDisplayer 使用 LinkStorage 来获取链接。我要测试的行为是“shouldDisplayLatestLinks”。在我的测试中,我是否需要模拟 LinkStorage,并让它返回具有模拟 getUrl() 等行为的模拟 Link 对象?

测试“叶子”类很容易,但我仍然很难找到测试其他类的方法。

【问题讨论】:

    标签: testing tdd mocking bdd


    【解决方案1】:

    简答:

    您应该模拟/存根不受 SUT 直接控制的任何内容。仅测试 SUT 的行为,切勿编写尝试确认超出该范围的行为的测试(即测试模拟/存根)。


    长答案:

    很难只见树木不见森林。如果您是编写所有代码的人,您有时会发现很难避免测试过于细化的实现细节。

    您想要测试行为的想法是正确的,因此当您开始考虑测试时,危险信号就出现了。这正是 TDD 的全部意义所在,因为它有助于暴露设计缺陷。请记住,只测试 SUT 的行为。其他一切都应该在您的单元测试(模拟)的控制之下,否则它就不是单元测试。

    将自己置于 LinkDisplayer 类的使用者的位置,并问自己“我将如何在生产代码中使用它?”您可能只是调用该方法并期望它起作用。您肯定不会调用数据库来确保它确实检索了正确数量的元素,或者它们是按类别排序的吗?那么为什么要尝试为此编写测试。

    调用displayLatestLinksByCategory的最终结果应该是什么?

    根据您的示例,我看到了该问题的两个可能答案,以及您应该根据这些答案采取哪些措施:

    1. 使用特定方法从数据库中获取一些东西,并将其显示在某处。
      • 如果这是该方法的作用,那么您应该测试的只有这两件事。主要是使用正确的参数在数据访问组件上调用正确的方法;并且返回的数据到达了它需要去的地方。
      • 您应该测试的是返回数据的形状***,因为这只是您的测试已经完全控制的测试行为。这就像测试您在测试中刚刚设置为 true 的变量实际上是否为 true。
      • 使用特定方法从数据库中获取一些东西并返回
      • 此时,各地的红旗都应该亮起。如果这是此方法的唯一好处,那么您必须问自己为什么不直接调用数据访问方法?作为此类的使用者,您已经可以控制正在使用哪个数据访问组件(您提供了它),所以为什么要有中间人。

    * 可以测试您的 SUT 如何响应特定形状的数据。如果返回 null 或集合为空,您可能希望抛出异常。如果返回的数量超过 N,您可能希望从列表中删除额外的值,但您永远不想测试超出 SUT 行为的内容。因为你可以直接控制从你的模拟返回的数据是如何形成的,所以测试可能会导致最糟糕的测试;一个不测试任何东西但仍然通过的测试。

    【讨论】:

      猜你喜欢
      • 2016-05-12
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 2014-06-21
      • 1970-01-01
      • 2013-01-16
      • 2021-03-15
      • 2016-03-15
      相关资源
      最近更新 更多