本文要点
微服务架构对在线(远程)依赖项的依赖较多,而对进程内组件的依赖较少,您的测试策略和测试环境需要适应这种变化。
当使用现有技术(如服务虚拟化)测试单体应用时,您不必同时测试所有内容;相反,您可以分而治之,测试单个模块或一组关系密切的组件。
当使用微服务时,还有几个选项可供选择,因为微服务通常部署在容器(如 Docker)环境中。
您需要管理相互依赖的组件,以便在测试微服务时可以有效地利用时间及控制成本。您可以在微服务测试中使用测试替身(Test Double)达到测试目的。
根据您的需要,您可以选用本文列出的其中一个选项或者多个选项的组合。
微服务架构和基于容器的基础设施的组合需要一个适合这个美丽新世界的测试策略。微服务架构对在线(远程)依赖项的依赖较多,而对进程内组件的依赖较少,您的测试策略和测试环境需要适应这种变化。
在线通信越多,测试微服务之间的连接和契约所需的精力就越多。此外,在迁移到基于容器的基础设施时,可以使用若干新的测试技术来处理采用微服务时经常出现的组件依赖。
从上市时间、成本和风险的角度选择测试技术。
当使用服务虚拟化等技术测试单体应用时,您不必同时测试所有内容。相反,您可以分而治之,测试单个模块或或一组关系密切的组件。您可以创建安全的隔离环境让开发人员测试他们的工作。在测试单体应用时,使用服务虚拟化让您可以将测试环境与相关组件解耦,并减少以下问题的影响:
难以准备或配置相关组件
测试数据准备成本高,耗时多
团队因为其他团队没有及时交付 API 而受阻
测试环境时间调度
当使用微服务时,您的选择更多,因为微服务通常部署在容器(如 Docker)环境中。在微服务架构中,您的团队可能会使用更广泛的测试技术。此外,由于微服务更多地通过网络进行通信,所以需要更彻底地测试网络连接的影响。使用更适合新架构的工具和技术可以缩短上市时间,降低成本和风险。
许多 IT 部门使用或维护着采用单体架构开发和部署的系统。典型的单体架构具有以下特点:
从事应用程序相关工作的人员被组织成独立的专家团队:UI 开发人员、中间件开发人员、后端开发人员、数据库管理员和系统管理员。
治理由架构师集中进行——例如,有一组用于代码质量、安全准则和测试方法的全局规则。
数据集中管理,因此,单体应用程序通常依赖于单个大型数据库。
自动化程度可能很低,有一些自动化测试,但是很少有基础设施自动化。
应用程序开发人员的组织结构经常会影响代码和测试环境的组织方式;这种效应被称为康威定律。通常,代码会被分成几个组件层,如 UI、服务和存储库。每个层都是一个单体,它们将被部署到共享环境中,通常包括开发、QA 和用户验收测试(UAT),参见图 1。
许多单体系统都是由职能竖井团队构建的,例如,运营团队是一个单独的实体,有单独的时间表。向这样的组织引入容器化方法需要做出变革,这可能会非常耗时,因为它涉及提供新的基础设施、培训员工以及为迁移到新方法创建迁移计划。
这意味着,从单体架构中解耦依赖项的技术常常局限于那些不需要容器的技术,它们运行在进程中,或者是在运营团队提供的已有的 VM 或硬件上。不需要容器化的技术包括:
根据康威定律,沟通模式复杂的职能竖井团队会创建出通信模式同样复杂的单体。这意味着用于服务虚拟化的工具必须非常强大,能够支持复杂的工作流和许多技术(例如,许多通信协议),这取决于被测系统(单体应用程序)的复杂性和测试用例的复杂性。
通常,会有一个单独的团队负责创建后端或第三方服务的 stub、mock 或虚拟服务。这经常会在负责服务虚拟化的团队中引起工作冲突,导致测试基础设施准备时间很长,维护成本很高。
在微服务架构中,你会发现:
团队是围绕业务功能组织的,例如由多名 UI、中间件和后端开发人员、数据库管理员和 DevOps 专家组成的跨职能团队;
去中心化治理,允许每个团队选择适合其工作的恰当工具;
去中心化数据管理,允许每个微服务或每一组相关的微服务管理自己的数据;
测试、部署和基础设施通常自动的,很少或没有人为干预。
这将影响到使用什么技术解耦微服务及其依赖项来达到测试目的。由于不需要满足所有团队需求的同构技术栈,所以每个团队通常都有更多满足其特定需求的选项可以选择。
对于微服务测试,解决方案主要是那些可以用于单体架构的解决方案(它们也适用于微服务)以及那些专门为微服务架构而设计的解决方案。
可用于单体架构的方法包括:使用测试替身,如stub、stub或虚拟服务;连接到后端或第三方系统的实际测试实例;契约测试。
可用于微服务架构的方法有测试容器(如数据库测试容器、服务虚拟化测试容器)、第三方服务测试容器(如 Redis 测试容器、ESB 测试容器或虚拟设备测试容器)和把遗留单体应用装盒。
关于如何编写微服务测试策略的文章和演讲已经有很多。下面是一些资源,供参考:
“微服务架构的测试策略”,Toby Clemson,ThoughtWorks(2014 年 11 月 18 日)
“微服务测试”,André Schaffer,Spotify(2018 年 1 月 11 日)
“微服务测试:从开发到生产”,Daniel Bryant,JAX London(2018 年 10 月 9 日)
您将使用许多用于单体架构的测试技术,并涉及容器的新技术。然而,不同测试技术的适用性可能会发生变化,因为微服务架构中的反馈循环更紧密,因为团队通常在一起并且是跨职能的。
上面列出的参考资料有一个共同的主题,即您需要管理相关组件,以便在测试微服务时可以有效地利用时间及控制成本。根据需要,您可以选择本中列出的其中一个选项,或者多个选项的组合。
在测试中使用依赖项
用于微服务测试的测试环境可能使用真正的依赖项,而不是测试替身。
在测试场景中,微服务可以与以下几种组件类型通信:
您可以使用另一个微服务的测试实例来测试您的微服务。例如,在测试微服务 A 时,将其连接到微服务 B 的测试实例并将它们一起测试。
您可以使用另一个微服务的生产实例来测试您的微服务。例如,在测试微服务 A 时,将其连接到微服务 B 的生产实例,并在将微服务 A 发布到生产环境之前将它们一起测试。
您可以使用第三方依赖项测试微服务。例如,在测试微服务 A 时,将其连接到第三方系统的生产实例。
您可以使用遗留的非微服务内部依赖项来测试微服务。例如,在测试微服务 A 时,您将它连接到已有的本地运行的大型机系统的测试实例。
您可以使用非软件(硬件)依赖项测试微服务。例如,在测试微服务 A 时,将其连接到负责实现服务的硬件设备。
除了上面列出的组件外,接下来,我们将列出可以在微服务测试中使用的特定于测试的依赖组件。
当然,您可以在微服务测试中使用所谓的测试替身,它们会冒充真正的依赖项。你有几种技术可以选择,这取决于依赖项的类型和手头的问题:
Mock(进程内或通过网络/远程)用一个特定于测试的对象替换微服务所依赖的对象,以验证微服务是否正确地使用了它。
Stub(进程内或通过网络/远程)用一个特定于测试的对象替换微服务所依赖的对象向微服务提供测试数据。测试数据可以是静态的,也可以是动态的。
模拟器(进程内或通过网络/远程)是 stub 的智能版本,它模仿微服务所依赖的系统的一些行为。例如,您不需要在测试中连接到真实的支付系统,而是可以连接到一个部分实现了可观测支付功能的模拟器。
服务虚拟化(通过网络/远程)也称为 API simulation 或 API mock。它的做法是,使用强大的服务虚拟化工具创建测试版本,替换真正的依赖组件。服务虚拟化工具提供一种类似模拟器的体验,但是减少了开发人员和测试人员的工作量。与为每个依赖项构建自定义的测试替身不同,有现成的工具可以处理常见的需要手写实现的样板功能。服务虚拟化工具提供的特性通常比 stub 或 mock 多,比如记录请求和响应,或者内置对HTTP、JMS、FTP或gRPC等多种技术的支持。
您可以使用一个内存数据库来代替真正的数据库实例用于测试。
您可以运行一个测试容器,即一个位于容器中的针对每个构建或管道的依赖项的测试实例,而不是使用跨多个团队、构建或管道共享的实例。例如,您可以使用数据库测试容器或服务虚拟化测试容器。
您可以把遗留单体应用装盒。您可以在容器中运行遗留系统,而不是依赖于共享的测试环境。您可以根据自己的测试需求来配置它。
契约测试
在使用类似微服务这样的松耦合组件时,契约测试是一个关键部分。
契约描述组件之间如何通信和交互,包括组件之间的消息格式(语法)和组件的行为预期(语义)。您可以使用契约测试来验证组件之间的契约是否得到执行,从而使您相信这些组件能够协同工作。当您使用特定于测试的依赖组件(例如测试替身)时,您还可以使用契约测试来确保它们符合最新的或任何特定版本的契约。以下是测试或管理组件之间的契约的几种方法:
在契约快照测试中,您的测试替身代表了组件之间在某个时间点上的契约快照。该快照可能会过时。您可以以自动化的方式测试契约快照。
契约快照刷新让您可以重新记录(刷新)组件之间的契约。通常,刷新要考虑语法,并在一定程度上考虑契约的语义。要进一步了解语法和语义测试,请参阅消费者驱动的契约测试。
消费者驱动的契约测试是完整微服务测试策略的一个组成部分。消费者驱动的契约分为生产者和消费者。消费者驱动的契约测试验证生产者是否提供了满足所有消费者期望的契约。消费者验证生产者是否提供了它们需要的消息结构和行为。
针对每个契约的小范围集成测试可以测试微服务中的连接器模块和依赖组件之间的契约。通常,在这种情况下,契约更多地是生产者驱动的,而不是消费者驱动的。
如果您想要两个依赖组件独立发布,请使用面向独立组件发布的契约测试。您必须记住测试最新工件和生产工件的组合。
端到端(E2E)测试意味着验证所有组件在完成用户旅程时都能很好地协同工作。这意味着组件之间的契约在跨系统执行用户旅程测试时得到了隐式验证。
总结
在测试微服务时,有许多管理依赖组件的技术。本文给出的信息应该可以填补一些空白,并帮助您定义开发和测试策略(包括测试金字塔)。
在第二部分中,我们将基于团队的成熟度、变化速度、上市时间、成本和风险来比较这些技术。
如果您发现本文有任何不清楚的地方,或者您有任何特定于项目的担忧或问题,请通过邮箱联系作者 Wojciech Bulaty (wojtek@trafficparrot.com)和 Liam Williams(Liam Williams at liam@trafficparrot.com)。
关于作者
Wojciech Bulaty专注于敏捷软件开发和测试架构。他在敏捷、自动化、XP、TDD、BDD、结对编程和整洁编码方面有十多年的实际编程和领导经验。他最新提供的服务是Traffic Parrot,通过提供 API mocking 和服务虚拟化工具帮助使用微服务的团队加速交付、提高质量和缩短上市时间。
Liam Williams是一位自动化专家,专注于使用高质量的软件解决方案改善容易出错的手工流程。他是一个开源贡献者和一些小型库的作者。最近,他加入了Traffic Parrot的团队,将注意力转向了迁移到现代微服务架构时测试体验的改善问题上。
原文链接:
Testing Microservices: Overview of 12 Useful Techniques - Part 1
评论