【问题标题】:Does unit testing on Services make sense? [closed]对服务进行单元测试有意义吗? [关闭]
【发布时间】:2016-04-02 12:41:09
【问题描述】:

我正在用 Java Spring 编写一个由层组成的应用程序。他们的职责是:

  • REST/Controller 层 - 处理传入请求、用户身份验证、解析请求正文并根据请求与服务层通信
  • 服务层 - 主要处理创建/编辑/删除实体之间的关联和实体的状态。与 DAO 层和“实用程序”通信
  • DAO 层 - 持久性,将保存/删除/查找请求的实体
  • 实体/对象? layer - 代表应用领域的对象,相当愚蠢的功能,主要用于跟踪关联
  • “实用程序”层 - 不是真正的层,提供诸如检查对象之间的权限之类的服务(对象 A 想要将对象 B 添加到对象 C,因为它需要权限 XYZ...)等

我在开发过程中一直在编写一些测试,现在我已经考虑过了,它们是一种集成测试。测试的一般流程可以描述为:

  1. 调用serviceX创建对象A
  2. 调用serviceX创建对象B
  3. 调用 serviceY 来处理对象 A 和 B
  4. 调用 serviceX 报告对象 A 和 B 的状态并检查是否一切正确

在每个步骤中,服务调用 DAO 层和保存/创建/获取的对象,然后对其进行操作,测试检查操作是否成功并符合预期。

我正在考虑正确的单元测试应该是什么样子。我知道我应该模拟测试服务所依赖的每个组件,以便我可以“隔离”我想要测试的行为。然而,服务的大多数方法实际上并没有“返回”任何东西,它们只是操纵实体。

然后我的问题是 - 对应用程序的层/服务进行单元测试是否总是有意义,或者将它们作为一个整体进行测试是否可以接受?


附录

其中一种方法的示例。这个负责将新用户添加到组中。添加成员必须由组的经理完成。所以逻辑是:

  • 获取参数 - manager、group、newUser
  • 如果任何参数为空,则抛出异常
  • 调用permissionHandler服务并检查manager用户在group中是否有足够的权限来完成这个动作
  • 检查newUser 是否已存在于组中。如果是,则抛出异常。
  • 如果一切正常,调用groupMemberHelper 服务创建一个新的GroupMember 对象,表示usergroup 之间的关联(以及有关关联的更多信息)
  • 将此GroupMember 对象添加到组中
  • 对于组已拥有的所有“项目”,调用帮助服务,在这些项目和新添加的用户之间创建链接。

为了测试此方法,我将跟踪 groupnewUser 以及此方法影响的所有对象,并检查状态“之前”和“之后”是否符合我的预期。

如果不依赖被调用服务的功能,我想不出另一种方法来测试它。我错了吗?

【问题讨论】:

    标签: java spring unit-testing layer


    【解决方案1】:

    我知道我应该模拟测试服务所依赖的每个组件,以便我可以“隔离”我想要测试的行为。

    这种广泛持有的观点是错误的。您不必总是嘲笑每个合作者。

    在测试我的表示层时,我使用模拟数据层和真实(非模拟)服务层。

    广泛持有的观点的错误在于指定的行为和实现细节之间存在差异。您的测试应该测试指定的行为。他们不应该假设任何特定的实现细节。在大多数情况下,没有指定高层与低层交互的方式,只是它的调用满足低层强加的任何先决条件。因此,如果您要使用模拟的较低层来测试较高层,则该模拟必须为其每个方法提供正确的行为,因为您不知道较高层将调用哪些方法。这可以使适当的模拟对象与真实对象一样复杂。在这种情况下,使用模拟不会给您带来任何好处。

    模拟数据层并不太繁重,因为您可以将其实现为内存存储,使用地图和列表,而不是数据库,并为所有服务层和表示层测试使用相同的模拟类。

    【讨论】:

    • 让我检查一下我是否理解正确。你会说一般的想法是,当测试一个层时,使用它下面的层的真实实现是“可以的”? (除了数据层,我同意它很容易模拟)
    • @Martin 是的,没关系。你必须明智地嘲笑哪些合作者,而不是盲目地认为你必须嘲笑一切。或者什么都不做。
    • 我喜欢 Martin Fowler 的演示文稿/文章,他展示了如何进行测试 - martinfowler.com/articles/microservice-testing。它应该是一个微服务测试指南,但我发现它实际上对大多数软件都非常有用。
    【解决方案2】:

    示例中的方法向您展示了您在服务中做了很多工作。这就是测试的副作用——它们显示出弱代码架构。尝试将验证逻辑移动到另一个 bean,将创建逻辑移动到工厂等等。您将拥有易于阅读、理解、维护和单元测试的代码。

    【讨论】:

    • 我正在考虑创建工厂服务,但后来我并不特别喜欢仅使用一种方法提供服务的想法,因此我将该功能合并为服务层的一部分。尽管我认为拥有工厂和流程是有意义的:调用服务来创建对象。这将创建委托给工厂,该工厂返回一个新对象,然后由调用适当数据层方法的服务持久化。谢谢
    猜你喜欢
    • 1970-01-01
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-07
    相关资源
    最近更新 更多