供给测结构性改革内容
有很多方法可以将整个应用程序分成多个包。 关于按功能或按层打包的优缺点的讨论可以在许多编程博客和论坛上找到。 我想从可测试性开始讨论这个主题,看看它是否会带来任何有意义的结果。
首先,让我们尝试描述我们通常希望跨不同层在应用程序中进行测试的内容。 让我们假设标准的三层体系结构。 在底部,我们有数据层。
根据我们对域驱动设计的态度,我们将尝试最大化(对于丰富的,面向业务的实体)或最小化(对于仅由getter和setter建立的贫乏实体)测试覆盖率。 在第二种方法中,甚至很难说出任何测试,除非您不信任Java并且想验证get是否可以检索set调用之前分配的值。 对于富实体,我们肯定要验证业务逻辑的正确性。 但老实说,几乎总是可以通过带有适当模拟设置的简单单元测试来完成。 在这一层中通常有成千上万的测试,因此我们希望它们能最快地完成。 这是单元测试框架的绝佳领域! 等待? 您为什么不想使用数据库测试实体? 我可以提出相反的问题-我为什么要这样做? 要验证JPA或任何其他持久性API是否仍在工作? 当然,总会有一些真正复杂的查询需要在下面的真实数据库中进行验证。 对于这些情况,我将在存储库级别使用集成测试。 只是数据库+存储库+实体。 但是请记住关于单一责任。 您的集成测试仅检查查询-将整个实体逻辑保留给单元测试。
下一步通常是根据服务构建的。 在DDD中,服务仅与存储库一起使用以加载实体并委派它们整个业务逻辑处理。 如您所料,这些测试将非常简单。 您认为我们这里需要数据库吗? 会提供任何附加值吗? 不要这样 那第二种情况呢? 模型中的贫血实体? 整个逻辑集中在服务上,因此我们必须在这一层中累积测试覆盖率。 但是,正如我们已经在域逻辑中讨论的那样,无需使用外部资源就可以完成此操作。 再过一次-我们需要的只是单元测试。 因此仍然没有数据库。 我们可以基于存储库模拟运行所有测试。 管理数据集没有问题,不会导致“预期3但发现2”测试失败。 仅仅是因为其他一些测试又提交了一个价值在200美元到300美元之间的订单。 即使我们想在这里使用IoC框架,它也可以使用模拟来模拟存储库层。 如果不与数据层框架进行适当的解耦,则会通过某种扫描机制自动加载存储库。 这不是我们想要的东西。
在服务之上,我们通常放置一些允许用户使用我们的应用程序的东西。 我们可以使用RESTful API,SOAP服务等作为前端。在这里检查什么重要? 与客户公平起见,我们应该遵守与客户签订的合同。 这对于单独的博客文章而言可能是重要的,但仅限于REST服务:
“如果您将POST请求发送到/ users URL,我将回答所有用户的列表。 每个用户的ID都是整数,带有用户名的字符串。”
好的–看起来像是合同。 那么我们应该在这一层检查什么呢? 当然,如果这份合同有效。 发送HTTP请求并验证响应是否包含用户数组,每个条目都是根据整数ID和字符串用户名从中构建的。 我们可以在服务模拟的基础上做到吗? 当然可以:)
所以封装一切:
- 数据层=用于逻辑和与DB的集成测试的单元测试,用于复杂的查询验证
- 服务层=用于逻辑和光集成测试的单元测试,无需使用DB用于测试IoC框架相关的逻辑
- 前层=无需DB即可验证客户合同的集成测试
到目前为止,我们已经详细描述了在不同级别上值得测试的内容。 现在,让我们转到基于功能的打包。 当围绕不同的业务环境构建代码时,绝对有助于使代码井井有条。 对于大型应用程序,它使您可以将其分解为许多模块甚至许多应用程序。 如果没有这样的功能布局,那么这些动作之前就需要进行大量的重构。 但是在将我们的整体拆分成应用程序之后,仍然需要吗? 考虑一下启动新应用程序。 它的基本包装是什么? com.my.company.application ? 它只不过是功能包:)但是,您会停在此基本包上还是还是分成几层? 如您所见,这两个结构可以一起生活。
对于基于层的结构,我们的应用程序如下所示:
com.company.application\.data\.config\.model\.repository\.service\.config\.api\.config\.controller
对于基于功能的我们会得到类似
com.company.application\.order\.client\.invoice
但是通常随着业务逻辑的不断发展,它导致将整个应用程序拆分为模块或服务,因此最终我们得到:
com.company.application.order\.data\.service\.apicom.company.application.client\.data\.service\.apicom.company.application.invoice\.data\.service\.api
总结一下。 我认为分层包装是必须的。 它使我们能够分别测试每一层并保持我们的测试井井有条。 按功能打包在较大的项目中确实很有用。 对于围绕单个绑定上下文构建的微服务,更详细的划分可能会导致导航不舒适。 但是,出于与上述相同的原因,功能包内的代码仍应在层上断开。 尤其是基于Spring Framework的基于层的结构,可以帮助我们设置有用的组件扫描,并且不会因为仅仅希望使用两个服务来启动上下文而驱使我们设置数据库。 在我的GitHub存储库https://github.com/jkubrynski/spring-package-structure中,您可以找到基于Spring的示例项目。
翻译自: https://www.javacodegeeks.com/2015/11/smart-package-structure-to-improve-testability.html
供给测结构性改革内容