在服务层进行单元测试是否有意义?

3
我正在使用Java Spring编写一个应用程序,它由多个层组成。它们的职责如下:
- REST/Controller层 - 处理传入的请求、用户身份验证、解析请求体并根据请求与Service层通信。 - Service层 - 大部分处理实体之间的创建/编辑/删除关联和状态。与DAO层和"Utility"层通信。 - DAO层 - 持久化,将保存/删除/查找所需的实体。 - 实体/对象?层 - 表示应用程序领域的对象,功能相对简单,主要用于跟踪关联。 - "Utility"层 - 不是真正的一层,提供服务,例如检查对象之间的权限(对象A想将对象B添加到对象C中,因此需要权限XYZ...)等。
在开发过程中,我编写了一些测试,现在想起来,它们有点像集成测试。测试的一般流程可以描述为:
1. 调用serviceX创建对象A 2. 调用serviceX创建对象B 3. 调用serviceY对对象A和B进行操作 4. 调用serviceX报告对象A和B的状态,并检查是否正确
在每个步骤中,服务调用了DAO层并保存/创建/获取对象,然后对操作进行检查,以确保操作成功且符合预期。
我正在考虑如何编写适当的单元测试。我知道我应该模拟被测试服务依赖的每个组件,以便我可以“隔离”我想要测试的行为。然而,大多数服务方法实际上并没有“返回”任何内容,它们只是操作实体。
那么我的问题是 - 是否始终有意义对应用程序的层/服务进行单元测试,还是将它们作为一个整体进行测试是可以接受的?
附录:
以下是其中一个方法的示例。这个方法负责将新用户添加到组中。添加成员必须由组的管理员完成。所以逻辑如下:
- 获取参数 - 管理员、组、新用户 - 如果任何参数为空,则抛出异常 - 调用permissionHandler服务并检查manager用户在group中是否具有足够的权限来完成此操作 - 检查newUser是否已经存在于组中。如果是,则抛出异常。 - 如果一切正常,请调用groupMemberHelper服务创建一个新的GroupMember对象,表示user和group之间的关联(以及关联的更多信息) - 将此GroupMember对象添加到组中 - 对于组已拥有的所有“项”,调用一个帮助器服务,创建这些项与新添加的用户之间的链接。
为了测试这个方法,我会跟踪groupnewUser以及所有受此方法影响的对象,并检查状态“之前”和“之后”是否符合我的预期。
我无法想到其他的测试方法,而不依赖于被调用服务的功能。我错了吗?
2个回答

3
我知道在测试服务时,应该模拟被测试服务所依赖的每个组件,以便“隔离”要测试的行为。然而这种广泛存在的观点是错误的。您不必总是模拟每个合作者。
当我测试表现层时,我使用模拟数据层和真实(非模拟)服务层。
广泛存在的观点的问题在于指定行为与实现细节之间存在差异。您的测试应该测试指定的行为。它们不应假设任何特定的实现细节。在大多数情况下,高级别层与低级别层交互的方式未被指定,除了其调用满足低级别层强加的任何前提条件。因此,如果您使用模拟的较低层来测试较高层,则模拟必须为其每个方法提供正确的行为,因为您不知道较高层将调用哪些方法。这可能会使适当的模拟对象变得像真实对象一样复杂。在这种情况下,使用模拟不会带来任何好处。
模拟数据层并不太繁琐,因为您可以将其实现为内存存储,使用地图和列表,而不是数据库,并且对于所有服务层和表现层测试都使用相同的模拟类。

让我确认一下我的理解是否正确。你的意思是,在测试一个层时,使用其下面的真实实现是“可以”的一种做法吗?(除了数据层,我同意这个层次相对容易模拟) - Martin Melka
@Martin 是的,可以这样做。你必须慎重选择哪些合作者需要模拟,而不是盲目地认为你必须模拟所有东西或者什么都不要模拟。 - Raedwald
我喜欢Martin Fowler的演示/文章,他展示了如何进行测试 - http://martinfowler.com/articles/microservice-testing/。它原本是一份微服务测试指南,但我发现它对大多数软件实际上非常有用。 - gmaslowski

2

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


我曾考虑创建工厂服务,但我并不特别喜欢只有一个方法的服务的想法,所以我将该功能作为服务层的一部分加入其中。尽管我猜想拥有一个工厂和流程是有意义的:调用服务来创建对象。这将把创建委托给工厂,工厂返回一个新对象,然后由服务调用适当的数据层方法进行持久化。谢谢。 - Martin Melka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接