使用消费者驱动的合同来减少对集成测试的需求

Nicola Adamchik.
- 匹兹堡,帕

在STICH修复manbetx万博体育app 官方下载,我们有许多不同的应用程序和服务。随着我们的基础架构增长,需要创建和定义更多“微服务”以集中和隔离重要的共享行为和数据。测试是我们开发过程的一个非常重要的部分。正如我们开始创造越来越多的服务,我们意识到我们必须改变我们在涉及服务时考虑集成测试的方式。以下是您可能熟悉的典型工作流程:

  1. 为预期的特性编写单元测试。
  2. 实施预期的功能。
  3. 更新单元测试以存根任何外部引用,以确保您的单元测试只验证我们正在测试的工作单元。(按照红/绿/重构)
  4. 使用验收和集成级别规格补充您的测试套件,以确保新功能完整地工作。

如果您在单元测试中存根服务呼叫,但集成测试尝试将达到服务端点的情况会发生什么?创建面向服务的架构将不断提高应用端点之间的复杂性和依赖关系。例如,如果我们的新服务依赖于另一个服务,那么依赖于另一个服务,那么您如何保留一致的状态,以便在本地进行测试,以及在您的持续集成平台上?您想要在所有应用程序和服务上设置Docker,只需运行一个测试套件?添加新服务是否有额外的复杂性,这意味着?

我认为有一个集成的测试环境与Docker可以非常有助于维护一些非常重要的用例;特别是如果你正在转向一个更加分布式的体系结构。然而,我认为有一种更好的方法来处理大多数测试需求,而不需要您的开发人员安装和学习一种新的平台技术。如何?通过使用消费者驱动契约(CDC)来支持编程接口的端点。

消费者驱动的合同允许服务的消费者来定义服务中的预期行为。然后,该服务可以将“链接”指定并验证它确实满足了期望。这允许您对测试套件具有高度信心,同时解耦实现细节并保持简单的事物。无需设置一个特殊的环境,每个服务都可以旋转自身版本并彼此交谈。

我们的网站和移动应用程序允许我们的客户安排一个新的“修复”(或发货)。假设我们想要编写一个能够为其中一个客户安排预订的服务。下面是一个示例,说明我们如何使用协议宝石:

在API客户端中,声明你与“scheduling service”有一个契约:

规范/ service_providers / pact_helper.rb

要求“spec_helper”要求'PACT / COUNDER / RSPEC'协议service_consumer.“想要安排的新应用程序!”has_pact_with“调度服务”mock_service.:scheduling_service.港口1234.结尾结尾结尾

这会创建一个将在我们的规范中使用的模拟服务。接下来,让我们定义我们的新应用希望如何与我们的“调度服务”交互:

spec / service_providers / scheduling_service_client_spec.rb

Request_relative.'pact_helper'#上面显示的文件要求“spec_helper”描述StitchFix::SchedulingClient.::客户端:协议= >真正的主题{描述_class.端点:'http:// localhost:1234'api_key:'3E61-0EDA-46EF-860'}:fix_request.{......}:client_id.{......}: request_header{......}描述“schedule_fix_request”语境'好的要求'#快乐路径场景: request_body{哈希::土豆泥fix_request:fix_requestclient_id:client_id.}: expected_okay{哈希::土豆泥预订:哈希::土豆泥地位:'好的')))}之前#定义服务的预期行为scheduling_service.给予“修复请求和客户ID”)。of_receiving“安排修复的请求”)。方法::帖子小路:schedule_a_fix_request_path.标题:request_header身体:Request_Body.)。will_respond_with.地位:201身体:{预订:{地位:'好的'}}结尾'如果一切都成功,返回ok '预计主题schings_fix_request.fix_requestclient_id.)。eq.expected_okay结尾结尾结尾结尾

在这个文件上运行rspec将验证客户机方法是否发送了正确的请求并返回预期的结果。如果测试通过,则生成服务的契约协议。pact协议只是上面定义的规范的JSON表示。这个文件将在您的测试套件每次运行时重新生成。它将是客户端和服务之间的共享信息。

以下是测试错误条件的另一个示例:

语境“缺失的修复请求”:missing_fix_request.{{}}: request_body_missing_fix_request{哈希::土豆泥fix_request:missing_fix_request.client_id:client_id.}之前scheduling_service.给予“一个请求缺少修复请求”)。of_receiving“安排修复的请求”)。方法::帖子小路:schedule_a_fix_request_path.标题:request_header身体:Request_body_missing_fix_Request.)。will_respond_with.地位:422身体:{错误:[{代码:“param_missing”信息:'param缺失或值为空:fix_request'})}结尾如果缺少修复请求,则提出错误预计{主题schings_fix_request.missing_fix_request.client_id.}。raise_error/ param丢失或值为空:fix_request /结尾结尾结尾

这就是我们需要为消费者应用程序所做的一切。

现在,由于我们已经生成了pact协议,我们可以在scheduling服务中添加验证代码。

首先,声明协议协议:

规范/ service_consumers / pact_helper.rb

要求'PACT / PROVITER / RSPEC'要求“./spec/service_consumers/provider_states_for_new_app”协议service_provider“调度服务”honours_pact_with'想要安排的新应用程序!'PACT_URI.协议::提供者::patturi.“上面生成的json文件路径”结尾结尾

接下来,为PAtt协议提供实现文件:

规范/ service_consumers / provider_states_for_new_app.rb

要求“spec_helper”协议provider_states_for“一个新的应用程序”Provider_State.“修复请求和客户ID”set_up#做任何你可能需要的测试设置:#示例:client = FactoryGirl.create(:active_client)# 或者no_op结尾结尾Provider_State.“丢失修复请求的请求”set_up#这将调用API请求,#让我们确保我们有一些测试数据可以使用客户=工厂女工创建:Active_Client.allow_any_instance_of.顾客)。收到:客户端)。and_return.客户结尾结尾结尾

请注意provider_state文本字符串是如何与客户端规范中的“给定”字符串匹配的。

执行'Rake Pact:验证'运行客户端PACT文件中指定的测试,并验证它根据定义的行为响应。如果该服务对规范进行了破坏性的变化,它将不再遵守刚刚写的PACT和测试将失败,而无需编写集成测试!

正如您所看到的,PACT GEM使其在面向服务的架构中的端点之间侧面很容易。我们遗漏的唯一棘手的事情是如何在您的消费者存储库和服务存储库之间管理共享JSON规范文件。但这是本文的范围。我建议你调查使用协议代理或者使用持续集成工具在构建之间传递共享资源。

这篇文章! 帖子在linkedin
多线程

来和我们一起工作吧!

我们是一个多元化的团队,致力于打造伟大的产品,我们希望得到你的帮助。你想和出色的同行一起创造出色的产品吗?加入我们吧!