继续关于测试的另一个但类似的问题(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'类很容易,但我仍然觉得很难找到测试其他类的方法。
答案 0 :(得分:3)
你应该嘲笑/抄袭任何不受你的SUT直接控制的东西。仅测试SUT的行为,并且从不编写试图确认超出该范围的行为的测试(即测试模拟/存根)。
可能很难看到森林中的树木。如果您是编写所有代码的人,您可能会发现有时很难避免测试过于细化的实现细节。
你有正确的想法想要测试行为,当你开始考虑你的测试时,正确的红旗就会消失。这正是TDD的全部意义,因为它有助于揭露设计缺陷。但请记住,只测试SUT的行为。其他一切都应该在你的单元测试(模拟)的控制之下,否则它不会是一个单元测试。
让自己处于LinkDisplayer类的使用者的位置,并问自己“我将如何在生产代码中使用它?”您可能只是调用该方法并期望它有效。你肯定不会调用数据库来确保它确实检索到正确数量的元素,或者它们是按类别排序的吗?那么为什么要尝试为此编写测试。
调用displayLatestLinksByCategory
的最终结果应该是什么?
我根据您的示例看到了该问题的两个可能答案,以及您应根据这些答案采取的行动:
* 可以测试您的SUT如何响应特定形状的数据。如果返回null,或者集合为空,则可能需要抛出一个exeption。如果返回的N超过N,您可能希望从列表中剪切额外的值,但您永远不想测试超出SUT行为的内容。因为您可以直接控制从模拟中返回的数据的形状,所以测试可能会导致最糟糕的测试;测试没有测试任何东西,但仍然通过。