Java 对服务进行单元测试有意义吗?

Java 对服务进行单元测试有意义吗?,java,spring,unit-testing,layer,Java,Spring,Unit Testing,Layer,我正在用JavaSpring编写一个由层组成的应用程序。他们的职责是: REST/Controller层-根据请求处理传入请求、用户身份验证、解析请求主体以及与服务层通信 服务层-主要处理实体和状态之间的关联的创建/编辑/删除。与DAO层和“实用程序”通信 DAO层-持久化,将保存/删除/查找请求的实体 实体/对象?层-表示应用程序域的对象,相当愚蠢的功能,主要用于跟踪关联 “实用程序”层-实际上不是一个层,提供诸如检查对象之间的权限(对象a希望将对象B添加到对象C,因为它需要权限XYZ…)等

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

  • REST/Controller层-根据请求处理传入请求、用户身份验证、解析请求主体以及与服务层通信
  • 服务层-主要处理实体和状态之间的关联的创建/编辑/删除。与DAO层和“实用程序”通信
  • DAO层-持久化,将保存/删除/查找请求的实体
  • 实体/对象?层-表示应用程序域的对象,相当愚蠢的功能,主要用于跟踪关联
  • “实用程序”层-实际上不是一个层,提供诸如检查对象之间的权限(对象a希望将对象B添加到对象C,因为它需要权限XYZ…)等服务
我在开发过程中编写了一些测试,现在我已经考虑过了,它们是一种集成测试。测试的一般流程可以描述为:

  • 调用serviceX创建对象
  • 调用serviceX创建对象B
  • 调用serviceY对对象A和B执行某些操作
  • 调用serviceX报告对象A和B的状态,并检查是否一切正常
  • 在每个步骤中,服务调用DAO层并保存/创建/获取对象,然后对这些对象进行操作,测试检查操作是否成功以及是否符合预期

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

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


    附录

    其中一种方法的示例。该用户负责向组中添加新用户。添加成员必须由组的经理完成。逻辑是这样的:

    • 获取参数-管理器、组、新用户
    • 如果任何参数为null,则抛出异常
    • 调用
      permissionHandler
      服务,检查
      manager
      用户在
      组中是否有足够的权限来完成此操作
    • 检查组中是否已存在
      newUser
      。如果是,抛出异常
    • 如果一切正常,请调用
      groupMemberHelper
      服务创建一个新的
      GroupMember
      对象,表示
      user
      group
      之间的关联(以及有关关联的更多信息)
    • 将此
      GroupMember
      对象添加到组中
    • 对于组已经拥有的所有“项”,请调用帮助器服务,该服务将在这些项和新添加的用户之间创建链接
    为了测试此方法,我将跟踪
    新用户
    以及此方法影响的所有对象,并检查“之前”和“之后”的状态是否与我预期的状态相同


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

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

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

    这种被广泛接受的观点是错误的。你不必总是嘲笑每一个合作者

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

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


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

    我正在考虑创建factory服务,但是我并不特别喜欢只使用一种方法提供服务的想法,所以我将该功能合并为服务层的一部分。尽管我认为有一个工厂和流程是有意义的:调用一个服务来创建一个对象。将创建委托给工厂,工厂返回一个新对象,然后由调用适当数据层方法的服务持久化该对象。谢谢,让我检查一下我是否理解正确。你会说一般的想法是,当测试一个层时,使用它下面的层的实际实现是“好的”吗?(除了数据层,我同意这很容易模仿)@Martin是的,没关系。你必须明智地让合作者嘲笑你,而不是盲目地认为你必须嘲笑一切。我喜欢Martin Fowler的演示/文章,他展示了如何进行测试-。它本应该是一个微服务测试指南,但我发现实际上它非常好