在许多方面,测试微服务应用程序与测试使用任何其他体系结构构建的应用程序没有什么不同。微服务面临的独特挑战是组成应用程序的服务数量之多,以及服务之间的依赖关系数量。
作为用于构建复杂系统的体系结构,微服务在开发社区中获得了巨大的关注。尽管人们开始意识到这并不是解决所有应用程序体系结构问题的灵丹妙药,但是那些与依赖项和扩展性相关的挑战共享的应用程序可以从中受益匪浅。
微服务的采用正在上升,但是与了解如何测试微服务相关的斗争也在增加。ThoughtWorks的Toby Clemson在列举您可能希望在微服务体系结构中使用的测试策略方面做得非常出色(请参阅他的文章,以获取有关您可能要创建的不同测试类型的详尽概述),但是有关如何建立和维护那些不同种类的测试仍处于起步阶段。
但是在许多方面,测试微服务应用程序与测试使用任何其他体系结构构建的应用程序没有什么不同。微服务使用众所周知的技术,例如REST或队列,为此软件行业已经拥有完善的测试工具和最佳实践。微服务面临的唯一挑战是构成应用程序的服务数量之多,以及服务之间的依赖性。此外,即使微服务依赖的其他微服务不可用或响应不正确,每个微服务仍需要正常运行。
微服务在相互交互时通常遵循两种模式:编排和响应式(编排)。许多微服务使用组合的“混合”方法。在本文中,我将提供一些策略来解决在使用这些不同模式的微服务创建自动化测试时出现的一些挑战,重点是针对单个微服务的测试(而不是整个应用程序的端到端测试) 。
使用业务流程的微服务将对外部服务或依赖项进行一个或多个显式调用。这些调用通常使用同步请求-响应流,并且通常将访问基于REST的服务。如果需要按特定顺序调用服务,则在收到对前一个服务的调用的响应之前,不会进行对后一个服务的调用。由于一项服务显式调用另一项服务,因此它们紧密耦合。
在上面显示的示例中,为投资组合微服务创建和执行测试具有挑战性,因为投资组合微服务依赖于帐户和报价微服务,而依赖项需要与投资组合微服务一起部署在测试环境中。Quotes服务依赖于第三方服务来检索实时股票价格,并且该服务返回的数据始终在变化。
依赖第三方服务或由不同团队开发的服务极大地增加了测试环境的复杂性。此外,还需要测试投资组合服务的任何意外行为,例如,当“帐户”和/或“报价”服务不可用,响应缓慢或响应意外数据时。重要的是,必须使这些服务以不同的意外行为响应,以验证Portfolio微服务正确处理了错误情况。
服务虚拟化急救!
您可以使用服务虚拟化来模拟“帐户和报价”微服务的响应。通过服务虚拟化,您可以定义“帐户和报价”微服务的虚拟版本,并将它们与投资组合微服务的实际实例一起部署。虚拟化微服务类似于虚拟化任何其他类型的服务或应用程序体系结构。它可能看起来像这样:
完成此操作后,就可以独立于其两个依赖项来测试Portfolio微服务。
下一个挑战是为不同的情况配置不同的环境,例如“帐户和报价”服务表现出预期的行为和意外的行为。假设团队要测试“帐户”服务或“报价”服务响应缓慢或出现错误情况时投资组合服务的行为。这可能需要运行至少5组不同的测试,其中每组都具有不同的环境配置,并考虑到响应时间慢、错误响应以及相关服务的正常和异常行为。
对于每次测试运行,都需要将环境放入正确的配置中,然后才能运行针对该配置的测试。在此示例中,我们最终进行了至少五次不同的测试运行,每种测试运行都有针对环境的自己的配置。Parasoft的持续测试平台中的Environment Manager模块可以为您管理以下不同的环境配置:
此过程并非特定于微服务架构-在面向服务的架构以及可能仅依赖少量服务的整体式应用程序中,通常会出现相同类型的问题。但是,在微服务架构中,从属服务的数量显着增加。在具有数十个或数百个服务的微服务环境中,针对不同的测试场景创建,管理和以编程方式在不同环境配置之间切换的能力非常重要,并且可以显着减少时间和精力。
管理协调微服务中的API更改
随着团队发展其微服务,不可避免的是将对服务进行API更改。API更改引起的关键问题是如何了解这些更改对服务使用者的影响。
当团队为他们正在构建的微服务修改API时,需要根据API中的更改来更新验证该微服务的所有测试。相反,如果使用虚拟服务来模拟从属微服务和用于这些从属微服务更改之一的API,则必须更新从属微服务的虚拟服务以反映API中的更改。
许多团队使用OpenAPI,RAML或其他服务定义来描述其微服务的API。使用服务定义时,Parasoft SOAtest和Parasoft Virtualize中的Change Advisor模块可以自动检测哪些API已更改,然后自动重构现有的功能测试或虚拟服务,以使用API中的任何新字段和/或删除的字段来更新它们。团队可以创建其服务定义的更新版本,并可以使用Change Advisor在进行更改之前了解更改对其测试和虚拟服务的影响。进行更改后,Change Advisor可使您轻松快捷地更新现有资产,以反映微服务中的更改。
微服务架构的主要目标之一是创建独立的组件。结果,部署、扩展和更新服务将变得更加容易。但是,当使用业务流程模式时,此目标并未完全实现,因为单个微服务直接依赖于其他微服务。解决此问题的一种方法是使用编排模式,也称为“反应式”或“事件驱动”微服务。在这种模式下,微服务不会直接相互引用。相反,它们将消息推送到其他微服务已订阅的事件流上。
请参见以下示例:
在此示例中,假设投资组合服务已被指示添加一个库存头寸。与其直接调用“帐户”服务,不如将事件发布到“已添加位置”事件流。帐户微服务已订阅该事件流,因此它获得了通知。它检查以确保用户帐户中有足够的资金。如果是这样,它将减少用户帐户中的资金数量,并将事件发布到“更新帐户”事件流中。如果用户的帐户中没有足够的资金,则它可能会将错误事件发布到其他事件流(为简化示例,未显示)。投资组合微服务已订阅“帐户更新”事件流,并且当看到“帐户”微服务发布的事件时,它随后基于来自帐户服务的确认更新其投资组合。
这种类型的体系结构中的异步通信带来的好处是,服务彼此之间高度解耦–可以替换,重新部署或扩展每个服务的实例,而无需其他微服务关心它们。折衷方案是事件的异步性质使得很难理解系统将如何执行以及事件的流向。根据产生的事件的顺序或种类,系统可能会以意外的方式运行。这被称为紧急行为,是在编排微服务的开发和测试中固有的挑战。
异步命令调用模式
在事件驱动的微服务的更广泛类别下,存在不同的异步消息传递模式。当需要使用异步操作来编排微服务时,可以使用异步命令调用模式——一种微服务需要异步调用另一种微服务,同时保证第二种微服务接收到消息。在这种模式下,通常使用队列交换消息。
微服务架构中用于实现此模式的常见框架是RabbitMQ。当一个微服务需要发布事件以供第二个微服务处理,然后等待从该第二个微服务读取“答复”事件时,就会发生这种模式的具体化身。
考虑我们刚才讨论的Portfolio示例,其中REST API调用告诉Portfolio微服务添加位置。投资组合服务将一个事件发布到“已添加位置”队列中,以供帐户微服务处理,然后等待帐户服务将回复事件发布到“帐户已更新”队列中,以便REST API调用可以返回从该事件接收的数据。有两种不同的方法可以为该示例配置测试方案:
第一种方法很简单,它使测试资产自成体系,而对测试基础结构没有任何其他外部依赖性。第二种方法是可重用的,并且是对系统实际行为的更紧密模拟。但是,第二种方法具有构建,部署和管理单独的虚拟资产的成本。
异步命令调用模式的一种变体是微服务,它在队列上侦听传入事件,处理该事件,然后在另一个队列上发布后续事件,以供一个或多个其他微服务处理:
在此示例中,发票微服务是需要测试的服务。付款服务将事件发布到“付款处理的RabbitMQ”队列中,以供发票服务提取。发票微服务从队列中读取事件,创建发票,然后将事件发布到“发票已创建”队列,以指导电子邮件微服务将带有发票的电子邮件发送给客户。要为发票微服务创建测试方案,可以配置一个包含两个RabbitMQ队列和已部署的发票微服务的测试环境。可以构造一个Parasoft SOAtest测试方案,该方案将付款处理的事件发布到Payment Processed队列中,然后该方案订阅``发票创建''队列以验证发票服务是否响应发布了正确的发票创建事件。非常简单的测试方案,可以很好地单独测试Invoice服务。
事件Firehose模式
当不同来源产生大量需要通过公共集线器快速交付给不同使用者的事件时,将使用事件Firehose模式。在这种模式中,消息是通过主题交换的(与异步命令调用模式中的消息是通过队列交换的消息相反)。Apache Kafka框架是用于实现事件firehose模式的常见框架,它看起来像这样:
假设我们要测试一个单一的微服务,该微服务订阅一个Kafka主题,处理它收到的事件,然后将其结果发布到另一个Kafka主题。例如,如下所示:
在此示例中,我们有一个预报微服务,该微服务订阅了“天气数据”主题,该主题从许多不同的来源收集许多不同的天气数据。然后,它将处理该数据以更新其预测模型,并将预测数据发布到“预测数据”主题。在这种情况下,我们需要验证天气预报服务是否将一组特定的天气数据事件的预期事件发布到了预报数据主题。
这可以通过配置具有两个Kafka主题和已部署的Forecast服务的测试环境来完成。测试场景将首先将必要的事件发布到“天气数据”主题,然后订阅“预测数据”主题以验证“预测”服务是否发布了正确的预测数据事件。需要构建多个不同的测试方案来验证可以预期由预测微服务处理的事件的不同类型和顺序。
这是一个相对简单的测试方案。预测微服务与其他微服务分离的事实带来了幸运的副作用,即预测服务的测试也与微服务分离。在这种情况下,您无需使用虚拟服务来建立复杂的环境,您只需创建发布事件的测试方案,并验证是否创建了正确的事件作为响应即可。
许多微服务团队已采用持续集成/连续部署(CI/CD)流程来构建,测试和部署容器化微服务,以使流程自动化并降低与部署更新相关的风险。
在此过程中,将自动创建一个包含微服务的容器映像并将其部署到测试环境中(通常由Kubernetes或基于Kubernetes的发行版(如OpenShift)进行管理),在该环境中可以在微服务推送到端到端之前对其进行验证。结束测试并最终投入生产。我建议阅读有关容器化微服务的CI/CD和设计微服务:持续集成。这两篇文章都很好地描述了这种过程。
我们讨论的一些测试模式依赖于对依赖的微服务使用虚拟服务,这些虚拟服务需要高度组件化并易于部署,其原因与它们模拟的微服务被组件化的原因相同。为了使服务虚拟化在这些环境中工作,您需要创建可以轻松部署的容器化虚拟服务。
要创建容器化的虚拟服务,您可以获取一个包含Parasoft Virtualize及其所有依赖项的基础映像,并用另一个包含该虚拟服务的所有虚拟资产配置的映像进行分层。可以将虚拟服务的新映像连同容器一起部署到Docker/Kubernetes环境中,并将其作为被测试微服务的容器及其所有(虚拟化)依赖项。
随着团队采用微服务,重要的是要了解如何充分测试它们。我在这里讨论的消息传递模式和相关测试模式并不是新事物,但是随着微服务变得越来越普遍和越来越多的应用程序,使用这些模式的需求已显着增长采用微服务范式。
为了以最大的灵活性为您的微服务创建和部署测试方案,您可以利用Parasoft SOAtest,Parasoft Virtualize和Parasoft连续测试平台来确保微服务的最高质量和可靠性。