使用 DAO 测试对象
继续关于测试的另一个但类似的问题(请参阅此处)。 我将使用类似的示例(伪代码),
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 对象?
测试“叶子”类很容易,但我仍然发现很难找到测试其他类的方法。
Continuing another but similar question about testing (see here). I'll use a similare example (pseudocode)
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()
So the linkDisplayer uses the LinkStorage to get Link(s). The behavior I want to test is 'shouldDisplayLatestLinks'. In my test, do I need to mock LinkStorage, and let it return mocked Link objects with mocked getUrl() etc behavior?
Testing 'leaf' classes is easy enough but I still find it very difficult to find my way in testing the other ones.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
简短回答:
您应该嘲笑/存根任何不受 SUT 直接控制的东西。 仅测试 SUT 的行为,切勿编写试图确认超出该范围的行为的测试(即测试模拟/存根)。
长答案:
只见树木很难见森林。 当您是所有代码的编写者时,您有时会发现很难避免对实现细节进行过于精细的测试。
您想要测试行为的想法是正确的,因此当您开始考虑测试时,危险信号就会响起。 这正是 TDD 的意义所在,因为它有助于暴露设计缺陷。 但请记住,仅测试 SUT 的行为。 其他一切都应该在单元测试(模拟)的控制之下,否则它就不是单元测试。
将自己置于 LinkDisplayer 类的使用者的位置,然后问自己“我将如何在生产代码中使用它?”您可能只是调用该方法并期望它起作用。 您肯定不会调用数据库来确保它确实检索到正确数量的元素,或者它们按类别排序,是吗? 那么为什么要尝试为此编写测试呢?
调用
displayLatestLinksByCategory
的最终结果应该是什么?根据您的示例,我发现该问题有两个可能的答案,以及您应该根据这些答案采取哪些操作:
* 可以测试您的 SUT 如何响应特定形状的数据。 如果返回 null 或集合为空,您可能希望引发异常。 如果返回的值超过 N,您可能希望从列表中删除多余的值,但您永远不想测试 SUT 行为之外的内容。 因为您可以直接控制从模拟返回的数据的形成方式,所以测试可能会导致最糟糕的测试; 一个不测试任何东西但仍然通过的测试。
Short Answer:
You should be mocking/stubbing anything that is not under the direct control of your SUT. Only test the behavior of your SUT, and never write a test that attempts to confirm behavior that is outside that scope (ie. testing mocks/stubs).
Long Answer:
it can be difficult to see the forest for the trees. When you are the one who wrote all the code you can find it difficult sometimes to steer clear of testing too granular of an implementation detail.
You have the right idea with wanting to test behavior, and rightly so red flags went off when you started thinking about your test. This is exactly what TDD is all about, as it helps to expose design flaws. Just remember though, only test the behavior of the SUT. Everything else should be under the control of your unit test (mocks), otherwise it would not be a unit test.
Put yourself in the position of the consumer of the LinkDisplayer class, and ask yourself "How would I use this in production code?" You would probably just call the method and expect that it worked. You most certainly would not make a call to the database to ensure that it did in fact retrieve the correct number of elements, or that they were sorted by category would you? So why attempt to write a test for that.
What should the end result be of calling
displayLatestLinksByCategory
?I see two possible answers to that question based on your sample, and what actions you should take based on those answers:
* It is ok to test how your SUT responds to specifically shaped data. You might want to throw an exeption if null is returned, or the collection is empty. You might want to clip extra values from the list if more than N were returned, but you never want to test something that is outside the behavior of your SUT. Because you are in direct control over how the data being returned from you mocks is shaped, testing that would lead to the worst kind of test possible; a test that doesn't test anything, but still passes.