使用 DAO 测试对象

发布于 2024-08-01 21:27:56 字数 670 浏览 2 评论 0原文

继续关于测试的另一个但类似的问题(请参阅此处)。 我将使用类似的示例(伪代码),

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

逆流 2024-08-08 21:27:56

简短回答:

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


长答案:

只见树木很难见森林。 当您是所有代码的编写者时,您有时会发现很难避免对实现细节进行过于精细的测试。

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

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

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

根据您的示例,我发现该问题有两个可能的答案,以及您应该根据这些答案采取哪些操作:

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

* 可以测试您的 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:

  1. Get some stuff from the database using a specific method, and display it somewhere.
    • If this is what that method does, then those are the only two things you should be testing for. Mainly, that the right method was called on the data access component with the right arguments; and that the data returned got to where it needed to go.
    • What you should not be testing is the shape*** of the data returned as this would only be testing behavior that your test is already in complete control over. It would be like testing that a variable you just set to true in your test is in fact true.
    • Get some stuff from the database using a specific method and return it
    • At this point red flags should be going off all over the place. If this is the only thing that this method is good for, then you have to ask yourself why you would not just call the data access method directly? As the consumer of this class you already have control over which data access component is being used (you provided it) so why have a middle man.

* 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文