使用DAO测试对象

时间:2009-08-12 18:59:23

标签: testing tdd mocking bdd

继续关于测试的另一个但类似的问题(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对象?

测试'leaf'类很容易,但我仍然觉得很难找到测试其他类的方法。

1 个答案:

答案 0 :(得分:3)

简答:

你应该嘲笑/抄袭任何不受你的SUT直接控制的东西。仅测试SUT的行为,并且从不编写试图确认超出该范围的行为的测试(即测试模拟/存根)。


长答案:

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

你有正确的想法想要测试行为,当你开始考虑你的测试时,正确的红旗就会消失。这正是TDD的全部意义,因为它有助于揭露设计缺陷。但请记住,只测试SUT的行为。其他一切都应该在你的单元测试(模拟)的控制之下,否则它不会是一个单元测试。

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

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

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

  1. 使用特定方法从数据库中获取一些内容,并将其显示在某处。
    • 如果这是该方法的作用,那么这些是您应该测试的唯一两件事。主要是,在具有正确参数的数据访问组件上调用正确的方法;并且返回的数据到达了它需要去的地方。
    • 您应该 进行测试的是返回数据的形状***,因为这只会测试您的测试已完全控制的行为。这就像测试你在测试中设置为true的变量实际上是真的。
    • 使用特定方法从数据库中获取一些内容并将其返回
    • 此时应该在整个地方出现红旗。如果这是这种方法唯一有用的东西,那么你必须问问自己为什么不直接调用数据访问方法?作为这个类的消费者,你已经可以控制使用哪个数据访问组件(你提供它),那么为什么要有一个中间人。
  2. * 可以测试您的SUT如何响应特定形状的数据。如果返回null,或者集合为空,则可能需要抛出一个exeption。如果返回的N超过N,您可能希望从列表中剪切额外的值,但您永远不想测试超出SUT行为的内容。因为您可以直接控制从模拟中返回的数据的形状,所以测试可能会导致最糟糕的测试;测试没有测试任何东西,但仍然通过。