【问题标题】:JUNIT Test for Service and DAO Layer [closed]服务和 DAO 层的 JUNIT 测试 [关闭]
【发布时间】:2014-03-26 04:48:54
【问题描述】:

我在项目中有分层架构,例如 Controller -> Service -> DAO。我想为服务层编写 JUnit 测试用例。既然服务层会在内部调用DAO方法,我为什么要为DAO层编写不同的测试用例,因为它是内部测试的。

有人说我需要使用 mockito 来模拟 DAO 方法。真的需要吗?测试服务层方法时不能直接使用原代码吗?

【问题讨论】:

    标签: unit-testing junit junit4


    【解决方案1】:

    为您的服务提供一组测试,并为 DAO 提供一组单独的测试。

    DAO 测试应该针对数据库,最好是内存数据库。这不仅仅是因为它很容易在测试中创建和拆除,而是因为你会知道它是你的,没有其他人会改变它。 DAO 测试将测试您的数据库映射和 SQL 是否正确。您可以使用 DBUnit 使用测试数据初始化数据库,并在每次测试结束时验证数据库的内容,因此每个测试都可以针对已知数据集运行。如果存在数据库级别的问题,则使用单独的 DAO 测试将更容易区分,因为您将在 DAO 测试中看到它们,而不是在服务测试中。

    可以使用 DAO 的模拟来测试服务。因为测试不使用数据库,所以它们执行得更快(针对数据库的测试运行速度要慢得多,即使使用内存数据库也是如此),并且您可以在不减慢测试速度的情况下练习许多不同的场景。服务层通常是您最复杂的地方,因此需要更多测试来覆盖它,如果这些测试能够快速运行会更好。

    通常情况下,您的 DAO 具有执行粗略操作的方法,并且这些方法在服务中以不同的方式被一遍又一遍地调用。当您将数据库访问限制为仅 DAO 测试时,您可以最大限度地减少测试所需的数据库访问总量,并且测试运行得更快。

    您所说的,有一组测试一直针对数据库(服务和 DAO)进行测试,这非常诱人,因为 a) 它一直针对真实事物进行测试,并且 b) 看起来像它会减少工作量(很多时候项目没有足够的抽象,你不能有效地单独测试东西,也没有时间把事情搞砸)。许多项目出于合理的原因做出相同的决定,并且它们都直接进入了相同的测试陷阱,这些测试需要很长时间才能运行并且会留下太多的案例(因为开发人员在现有测试时不愿意添加更多的测试案例)已经花费了太长时间)。缓慢的测试会导致糟糕的代码覆盖率和无效的测试。

    【讨论】:

    • 我观察到使用固定的测试数据并针对这些数据编写测试是一个坏主意。它迟早会回击。原因是测试数据隐含地涵盖了未以明显方式暴露的业务约束。当业务约束发生变化时,您必须找到并修复所有违规行为。此外,测试不是独立的。当您阅读测试时,您的数据库设置对您是不可见的。您必须猜测数据库中包含哪些数据。测试倾向于根据测试数据断言结果大小,这会在添加更多测试数据时中断。继续..
    • 对我的测试意见感兴趣的人,请查看我的posts
    【解决方案2】:

    在我看来,您应该尽可能避免使用 Mocks。避免使用 Mocks 的原因是:

    • 您必须确保您的模拟符合您模拟的 DAO 接口定义的合同。
    • 阅读模拟测试比阅读使用真实实现的简单单元测试要困难得多。因此,您的测试更有可能本身包含错误或具有逻辑流程。一个好的测试是一种易于理解和验证它(可能是)正确的测试。

    这并不意味着您永远不应该使用 Mocks,但您应该尽可能地喜欢真正的实现。

    另一方面,每当一个实现需要重量级的实现或数据库、JNDI 或 JMS 等资源时,您都想使用 Mocks。否则,您的测试始终是一种集成测试,需要重量级的设置并减慢您的测试套件。

    我的建议是为每种需要重量级资源(如数据库、JNDI、JMS 等)的类型提供内存实现。为确保它们是真正的替代品,符合合同要求,它们必须通过与实际实现相同的测试.因此,必须对这些合同的实现进行全面的测试覆盖!

    从现在开始,您应该可以在需要的地方使用真正的实现。对于所有难以测试的情况,例如网络错误、数据库连接或协议,我们使用 Mocks。它们在这种情况下是最好的,但仅在我看来应该简化为这些情况。

    【讨论】:

    • 如何“确保您的模拟符合您模拟的 DAO 接口定义的合同”。避免使用模拟的论点?即使有一个真正的实现,你也必须这样做。
    • 是的,你必须通过真正的实现来做到这一点。但我观察到许多开发人员模拟了 DAO,例如 DAO,没有确保模拟的 DAO 的合同符合合同。确保意味着,有一个测试表明模拟的 DAO 通过了与实现相同的测试。这是我很少观察到的。
    • 在语法方面与接口的合规性并不难确保,但行为却是。在我看来,你可以做到这一点的唯一方法是在使用模拟作为实现的替代之前,针对同一个测试运行实现和模拟。否则它们的行为可能会有所不同,并且您的基于模拟的测试将通过,直到您执行集成测试。你可能会花一些时间愉快地调试......这是丑陋和令人沮丧的。
    • 好的,我明白你的意思了。但是IMO,这是对模拟的不好使用。如果我理解正确,那将是例如,在某些情况下抛出异常的真实实现,但模拟不会。
    • 嗨,如果有一个 DAO,为什么不假装它,假设你有一个 servlet 调用了 20 个参数,然后你调用了 100 个不同的对象,你使用了 DAO,JMS,JNDI,如果您使用模拟的请求和响应调用该 servlet,而不是模拟 DAO 和 JMS,只是在 @BeforeClass 中伪造它们,您的整个执行将得到测试,最后只需将连接 2 连接到您的 testdb 并查看您期望的值,这不是更好吗来了没?
    猜你喜欢
    • 2012-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-31
    • 2014-12-24
    相关资源
    最近更新 更多