基于http的合约测试gamesys

Abstract

抽象

Contract Testing: a modern testing strategy much-discussed, though ultimately evasive of a universal understanding. Let’s get academic and wordy: it’s become an umbrella term for describing tests between disparate, heterogenous components that interact via some binding interface. This interface is described by a contract, a specification of sorts, which defines the inputs and outputs of a given provider. In layman’s terms, it’s testing that two components are speaking the same, agreed-upon language.

合同测试:尽管最终逃避了对通用性的理解,但现在讨论的是现代的测试策略。 让我们变得学术化和word琐:它已成为一个笼统的术语,用于描述通过某种绑定接口进行交互的各种异构组件之间的测试。 该接口由合同(一种规格说明)描述,该合同定义了给定提供者的输入和输出。 用外行的术语来说,它正在测试两个组件在说相同的,已达成共识的语言。

In distributed architectures, systems are typically composed of many components, and component interactions within a given system can be likened to vertices and edges. The vertices, of course, represent the individual components, and the edges represent the communication mechanisms that bridge two vertices together. The edge between two connecting vertices can be represented by a contract — a human-readable specification for bidirectional communication over a network, typically some abstraction above TCP (i.e. HTTP), but occasionally even UDP. In essence, a contract should be viewed as network-specific, whilst the components are language-agnostic.

在分布式体系结构中,系统通常由许多组件组成,并且给定系统内的组件交互可以比喻为顶点和边缘。 当然,这些顶点代表各个组件,而边缘则代表将两个顶点桥接在一起的通信机制。 两个连接顶点之间的边缘可以用契约来表示, 契约是人类可读的网络双向通信规范,通常是TCP(即HTTP)之上的某种抽象,但有时甚至是UDP。 本质上,合同应被视为特定网络的 ,而组件则与语言无关

Image for post
Figure 1A: Edges and vertices
图1A:边和顶点

A contract can come in many different flavours: it could be a hastily-written, infuriatingly error-strewn Word document provided by some junior engineer, a 300-page tedium-riddled PDF littered with SOAP examples or, preferably for REST over HTTP, an accurate and concise OpenAPI contract. With today’s focus on the latter, what exactly are we trying to test? At Gamesys, we have concocted a number of different patterns that extend and implement the concept of Contract Testing. One of those patterns, which is the focus of today’s article, is HTTP-Based Contract Testing.

合同可以有很多不同的风格:它可能是由一些初级工程师提供的草草编写的,令人发指的错误文档,一个300页的枯燥乏味的PDF文档,上面充斥着SOAP示例,或者对于REST over HTTP,最好是准确简洁的OpenAPI合同。 在今天关注后者的情况下,我们到底要测试什么? 在Gamesys,我们炮制了许多不同的模式 ,这些模式扩展并实现了合同测试的概念。 这些模式之一是基于HTTP的合同测试 ,这是今天文章的重点。

Actor Glossary

演员词汇表

Before deep-diving into the murky waters of HTTP-Based Contract Testing, it’s worth familiarising yourself with the cast. There are four main actors:

在深入探究基于HTTP的合同测试的晦涩环境之前,有必要熟悉一下演员表。 主要角色有四个

Contract: The specification of a given interface. For the purposes of this article, we are referring specifically to OpenAPI contracts, and will use the terms interchangeably. This should be considered a runtime-binding agreement between a provider and her consumers, as well as a documentation artifact. Provider: A HTTP-based application that provides a service.Consumer: A HTTP-based application that makes service requests to a provider.Contract Repository: A centralised store for contracts, accessible from all consumers and provider.

合同 :给定接口的规范 。 出于本文的目的,我们特指OpenAPI合同,并将互换使用这些术语。 应该将其视为提供者与其使用者之间的运行时 绑定协议以及文档工件。 提供程序:提供服务的基于HTTP的应用程序。 使用者:基于HTTP的应用程序,向提供程序发出服务请求。 合同存储库:合同的集中存储,所有消费者和提供者都可以访问。

A rough-and-ready topology of how these actors interact is illustrated below.

这些角色如何交互的大致拓扑如下所示。

Image for post
Figure 1b: Contract Testing Actor Topology
图1b:合同测试参与者拓扑

The Abridged & Academic Version

精简版和学术版

Provider-Side Contract Testing: During a build, providers MUST run provider-side contract tests to ensure the accuracy and syntactic correctness of a given contract. Provided these tests pass, the contract should be subsequently pushed to a centralised contract repository (whether that be Artifactory, Github, etc.)

提供商方合同测试:在构建过程中,提供商必须运行提供商方合同测试,以确保给定合同的准确性语法正确性 。 如果这些测试通过,则应随后将合同推送到集中的合同存储库(无论是Artifactory,Github等)。

Additional steps should also be taken to ensure that, in accordance with semantic versioning, contracts MUST NOT be pushed to the centralised contract repository, for a given major version, if it contains breaking changes. The syntactic correctness of a given contract, and semantic versioning checks, are not within the scope of this article.

还应该进一步采取措施,确保按照语义版本,合约绝不能推到中央存储库的合同,对于一个给定的主要版本,如果它包含重大更改。 给定合同的语法正确性和语义版本检查不在本文讨论范围之内。

Image for post
Figure 1c: The build lifecycle for provider-side contract tests
图1c:提供方合同测试的构建生命周期

Consumer-Side Contract Testing: During a build, consumers MUST pull the latest contract for a given, major API version and run consumer-side contract tests to ensure they interact appropriately with a given provider, as specified by a contract.

消费者方合同测试:在构建过程中,消费者必须为给定的主要API版本提取最新合同,并运行消费者方合同测试,以确保他们与合同规定的给定提供者进行适当的交互。

Image for post
Figure 1d: The build lifecycle for consumer-side contract tests
图1d:消费者方合同测试的构建生命周期

Context

语境

At Gamesys — between all the legacy system patches and regulatory deadlines thrust onto us— we are aggressively transitioning to a microservice-based architecture. This is not a silver bullet architecture, despite becoming a resumé buzzword in recent years. If products have sensible, logical boundaries, and teams are organised appropriately around these verticals, companies can enjoy a faster time-to-market, greater team autonomy and a higher frequency of deployments to production as a direct result of this architecture. Gone are the days of a meticulous release cadence, a bi-weekly scrum-of-scrums and launch-day panic.

在Gamesys上-在所有遗留系统补丁和规定的最后期限之间,我们正在积极过渡到基于微服务的体系结构。 尽管近年来成为简历的流行词,但这并不是万灵药。 如果产品具有合理的逻辑界限,并且围绕这些垂直领域适当地组织了团队,则由于此架构的直接结果,公司可以享有更快的上市时间,更大的团队自主权和更高的生产部署频率。 一丝不苟的发布节奏,每两周一次的混乱和发布日的恐慌已经一去不复返了。

Historically, our main development platform at Gamesys has been technically-partitioned — disregarding middleware and networking infrastructure, we had a front-end monolith and a back-end monolith, with a single database. This, of course, was a reflection of our organisational structure — as Conway’s Law famously suggests. To oversimplify, our platform was essentially one giant CRUD application which, whilst problematic in regards to deployments and inter-area organisational dependencies, meant most interfaces, or contracts, were enforced at compile-time.

从历史上看,我们在Gamesys上的主要开发平台已在技术上进行了分区 -不考虑中间件和网络基础架构,我们拥有一个前端数据库和一个后端数据库,以及一个数据库。 当然,这反映了我们的组织结构-正如康韦定律著名的那样。 为了简化起见,我们的平台本质上是一个巨型CRUD应用程序,尽管在部署和区域间组织依赖性方面存在问题,但这意味着大多数接口或合同都是在编译时执行的。

Undertaking this fine-grained componentization of our platform, of course, means intra-process communication evolves into inter-process communication. Contracts are no longer enforced by compile-time interfaces, with development violations flagged up by our IDEs. Instead, our platform becomes chatty, our network bandwidth becomes saturated, and compile-time problems become runtime problems.

当然,对我们平台进行这种细粒度的组件化意味着进程内通信演变为进程间通信。 合同不再由编译时接口强制执行,IDE标记为开发违规。 相反,我们的平台变得混乱,我们的网络带宽变得饱和,并且编译时问题变成了运行时问题

And let’s immediately focus on the first rule of networks: they don’t work. Networks are fallible — packets will arrive out of order, communication will become unbearably slow, and you will even have to handle the occasional bogan.

让我们立即关注网络的第一条规则: 它们不起作用 。 网络是容易犯错- 数据包到达的顺序,沟通变得不能忍受缓慢,你甚至必须处理偶尔博甘。

With this in mind — is it tenable to continue development of ‘spin-up-the-world’ style integration tests in the modern, micro-service era? You’re no longer launching two components, and testing their integration — a given user journey may require 10+ disparate components, all communicating over the wire. Some of these components may have transitive, middleware dependencies (e.g. Kafka) that, actually, you really, really don’t care about, but have to spin up anyway. Which transitive dependencies should you mock? Why should you care? What was once spinning up two, albeit heavy, applications, and testing their integration, has evolved into spiderweb, entropic chaos.

考虑到这一点—在现代微服务时代继续开发“世界上的旋转”式集成测试是否可行? 您不再需要启动两个组件并测试它们的集成-给定的用户旅程可能需要10多个不同的组件,所有组件都通过有线方式进行通信。 其中一些组件可能具有可传递的中间件依赖项(例如,Kafka),实际上,您确实并不在乎,但无论如何都必须旋转。 您应该嘲笑哪些传递依赖项? 你为什么要在乎呢? 曾经将两个应用程序(尽管很繁重)分拆并测试它们的集成的东西已经演变为蜘蛛网,这是混乱的熵。

Image for post
Figure 1e: This might have once been a monolith, with compile-time interfaces enforced…
图1e:这可能曾经是一个整体,并带有编译时接口…

And with that scaremongering out of the way…

随着那令人恐惧的骚扰……

The Goals

目标

With HTTP-Based Contract Testing, we wanted to ensure that:

借助基于HTTP的合同测试,我们希望确保:

  • Providers MUST adhere to the contract they provide;

    提供者必须遵守他们提供的合同;

  • Consumers MUST adhere to the contract they consume;

    消费者必须遵守他们所消费的合同;

  • Providers MUST NOT make breaking changes to the contract they provide (though this is out-of-scope for this article)

    提供者不得对其提供的合同进行重大更改(尽管这不在本文讨论范围之内)

Provider-Side Contract Testing

提供方侧合同测试

Ultimately, a contract is a representation, or a specification, of the inputs and outputs of a given application or service. It’s development-time documentation, and as such, it’s of paramount importance that the contract is indeed accurate. If a contract allows you to GET a fish, it would be really disappointing to find yourself dealing with an elephant at runtime.

最终,合同是给定应用程序或服务的输入和输出的表示或规范。 这是开发时间的文档,因此,合同确实准确至关重要。 如果合同允许您获取一条鱼,那么发现自己在运行时与大象打交道实在令人失望。

How we construct these contracts is of very little relevance. At Gamesys, we have experimented with both auto-generated contracts (using OpenAPI 3 Java annotation libraries), and hand-crafted contracts. Both have pros and cons but, ultimately, the output — syntactically and semantically accurate contracts — should remain the primary focus.

我们如何构建这些合同几乎没有任何意义。 在Gamesys,我们尝试了自动生成的合同(使用OpenAPI 3 Java注释库)和手工制定的合同。 两者都有优点和缺点,但最终,语法和语义上准确的合同输出应该仍然是主要重点。

Admittedly, ‘accuracy’ is a loaded word, so let’s drill a little deeper:

诚然,“准确度”是一个负载词,因此让我们更深入地进行研究:

  • Is my application serving the correct URLs, given the correct HTTP verbs, as specified by the contract?

    给定合同指定的正确HTTP动词,我的应用程序是否提供正确的URL?
  • Is my application serving/creating resources as specified by the contract?

    我的应用程序是否按照合同规定提供/创建资源?

This is where the wonderful Dredd comes into the fore. Dredd, for the purposes of our provider-side contract tests, assumes the role of a consumer. Given an OpenAPI v2/v3 contract, and the URL to your running application, it will fire a series of requests that are syntactically correct as per your contract. During a given test, as long as your application is capable of handling those requests, and returning responses that adhere to the contract specification, the test passes and, crucially, we can confidently assert our contract is accurate.

这就是奇妙的Dredd脱颖而出的地方。 就我们的提供商方合同测试而言,Dredd承担着消费者的角色。 给定一个OpenAPI v2 / v3合同,以及正在运行的应用程序的URL,它将触发一系列根据您的合同在语法上正确的请求。 在给定的测试过程中,只要您的应用程序能够处理这些请求,并返回符合合同规范的响应,那么测试就会通过,并且至关重要的是,我们可以确信我们的合同是准确的。

Image for post
Figure 1f: Provider-Side Contract Testing
图1f:提供方合同测试

It’s important to note that we do not want to conflate business rules with these tests. Let’s illustrate this with an example — we’re creating a competitor for Medium. We have an endpoint that allows you to create (POST) an article. When we first launch this service, we’d like to undercut Medium so, as long as you have one forum post, you can post an article.

需要注意的是,我们希望与这些测试混为一谈业务规则是很重要的。 让我们用一个例子来说明这一点-我们正在为Medium创建一个竞争对手。 我们有一个端点,使您可以创建(POST)文章。 首次启动此服务时,我们希望对Medium进行削弱,因此,只要您有一个论坛帖子,就可以发表文章。

Our capricious product-owners, blind-sided by our success, decide to make amendments to our business rules — to create an article, users must now have twenty forum posts. Our contract tests should be entirely insulated from this change — the specification, or interface, remains unaltered. Yes, the prerequisite requirements for interacting with said endpoint have changed, but the inputs and outputs remain unchanged.

我们反复无常的产品所有者对我们的成功视而不见,决定修改我们的业务规则-要创建文章,用户现在必须有20个论坛帖子。 我们的合同测试应完全不受此更改的影响—规格或接口保持不变。 是的,与所述端点进行交互的先决条件已更改,但输入和输出保持不变。

A well-tested application would have additional behaviour-driven tests that need amending, rather than contract tests. It’s the much-quipped separation of concerns — the primary focus of provider-side contract testing is to ensure our contracts are accurate. Most enterprises have a sensible testing taxonomy defined, and tests should have bounded concerns, otherwise we may find ourselves mindlessly sleepwalking back into the brittle tests of yesteryear.

一个经过良好测试的应用程序将具有其他需要修改的行为驱动测试,而不是合同测试。 这是顾虑重重的分离问题–提供方端合同测试的主要重点是确保我们的合同准确。 中号 OST企业定义了一个明智的测试分类,并测试应该有界的关注,否则,我们可能会发现自己盲目梦游回到昔日的脆性试验。

As Figure 1f attempts to illustrate, we isolate our provider-side contract tests from evolving business rules by mocking out any service layers, where business rules reside, in our spun-up application. In a horizontally-layered, technically-partitioned application architecture (e.g. MVC), this is simple to achieve. Dredd will make requests, and spun-up application will handle the requests with a mocked service layer thereby ensuring, by virtue of stubs, that any rule changes in this layer will have no impact on our tests.

如图1f试图说明的那样,我们通过模拟旋转应用程序中业务规则所在的任何服务层,将提供商端合同测试与不断发展的业务规则隔离开来。 在水平分层,技术分区的应用程序体系结构(例如MVC)中,这很容易实现。 Dredd将发出请求,而启动的应用程序将通过模拟的服务层处理请求,从而借助存根确保该层中的任何规则更改都不会影响我们的测试。

Okay, so we’ve run provider-side contract tests and they’ve come up green — what now? As consumer-driven contract testing rightly stresses, the value of providers is at consumption. In our case, a contract is both human-readable and machine-readable documentation for how to interact with a given provider. We need to make it available for consumption by consumers.

好的,所以我们已经运行了提供商端合同测试,并且已经通过了绿色测试-现在该怎么办? 正如消费者驱动的合同测试正确地强调的那样,提供者的价值在于消费 。 在我们的案例中,合同既是人类可读的文件,也是关于如何与给定提供者进行交互的机器可读文件。 我们需要使它可供消费者消费。

Ostensibly, this should be as simple as pushing to a binary repository manager, such as Artifactory, and going on our merry little way. Unfortunately, due care must be exercised. How do we indicate to consumers what the latest version of a contract is? Is latest synonymous with released? What if an application is rolled back on production (and thus, the contract that corresponds to that version is no longer in active use)?

从表面上看,这应该像推送到二进制存储库管理器(如Artifactory)那样简单,并且进行得很少。 不幸的是,必须谨慎。 我们如何向消费者表明最新的合同是什么? 最新发布的同义词吗? 如果应用程序已在生产中回滚(因此,与该版本相对应的合同不再有效使用)怎么办?

Pushing to our Centralised Contract Repository

推进我们的集中合同库

For context, as part of our deployment process, we use release gates at Gamesys — a scattering of *-build, *-rc and *-release repositories in Artifactory. In essence, these correspond to the different development environments that our binaries have been tested on. What’s important to reinforce, once again, is that a contract is a specification of an application’s inputs and outputs — almost like a shallow copy of a given provider, and given that we have robust provider-side contract tests in place, we can guarantee that the two do not diverge.

就上下文而言,作为部署过程的一部分,我们在Gamesys中使用发布门 -Artifactory中分散的* -build* -rc* -release存储库。 从本质上讲,它们对应于我们的二进制文件经过测试的不同开发环境。 再次强调的重要一点是,合同是对应用程序输入和输出的规范 -几乎就像给定提供者的浅表副本一样,并且鉴于我们已经进行了可靠的提供者方合同测试,我们可以保证两者不分歧。

With that said, it was decided that, as our contract is indeed a reflection of the application, we would promote our contracts through release gates in an identical manner. We created repositories that aligned with a given contract name and major version. For example, we would push our contracts to contract-repository-build/registration-api-v1 following tests in our build. When promoting our application from build to rc, we would similarly promote to contract-repository-rc, and so forth.

话虽如此,我们决定,由于我们的合同确实反映了申请,因此我们将以相同的方式通过释放门来促进我们的合同。 我们创建了与给定合同名称和主要版本一致的存储库。 例如,在构建过程中进行测试之后,我们会将合同推到contract-repository-build / registration-api-v1 。 当将我们的应用程序从build升级到rc时,我们将类似地升级到contract-repository-rc ,依此类推。

We found aligning the promotion strategy of a contract with the application itself somewhat reduces the cognitive complexity of contract testing — if you know how an application is promoted through the various release gates, there’s nothing to relearn for contract artifacts. An application and contract are, theoretically, inseparable artifacts, and treating them both as equal citizens reinforces this belief.

我们发现,将合同的升级策略与应用程序本身相结合会在某种程度上降低合同测试的认知复杂性-如果您知道如何通过各种发布门来升级应用程序,则没有什么可以重新学习合同工件的。 从理论上讲,应用程序和合同是不可分割的人工制品 ,将它们视为平等的公民可以加强这种信念。

This also ensures that concerns such as rollback are handled appropriately, in-line with your existing enterprise strategy— as long as consumers are able to accurately identify the latest version of a contract (e.g. through timestamp metadata), then we alleviate the concerns of consumers erroneously pulling an out-of-date contract and testing against them. Of course, this is why it’s important that you enact a mature methodology for identifying the live version of a given contract, whether that’s via a timestamp, custom tag or latest minor/patch version.

这还可以确保按照您现有的企业策略适当处理回滚之类的问题-只要消费者能够准确地识别最新版本的合同(例如通过时间戳记元数据),那么我们就可以减轻消费者的担忧错误地拉了一个过时的合同并对它们进行测试。 当然,这就是为什么必须制定成熟的方法来识别给定合同的实时版本的原因很重要,无论是通过时间戳,自定义标签还是最新的次要/补丁版本。

Consumer-Side Contract Testing

消费者方合同测试

Unlike a provider, a consumer can consume from many different providers. Consumers often act as aggregators, accumulating data from various different sources and making sense of them in the context of the domain they represent. This doesn’t present any issues as such, but axiomatically infers a linear distribution of tests correlating to the number of providers you consume from. That might be a lot of tests.

与提供者不同,消费者可以从许多不同的提供者进行消费。 消费者通常充当聚合器,从各种不同的来源收集数据,并在他们所代表的领域的背景下理解它们。 这样并不会带来任何问题,但是从理据上可以推断出与您从中使用的提供程序数量相关的测试的线性分布。 那可能是很多测试。

With that caveat out of the way — for those applications that consume from hundreds of providers — let us dig deeper. Prism is the tool-of-choice for running these tests at Gamesys. In a nutshell, Prism assumes the role of a provider — you provide it an OpenAPI contract, and it will spin-up a server that handles requests as per the contract specification.

对于那些要从数百个提供程序中使用的应用程序,这一警告已不再困扰我们了。 Prism是在Gamesys上运行这些测试的选择工具。 简而言之,Prism扮演提供者的角色—您为它提供一个OpenAPI合同,它将旋转一个服务器,该服务器根据合同规范来处理请求。

The general idea is that, in a well-crafted application that somewhat adheres to the single responsibility principle, you cherry-pick a client from your codebase — a client whose sole responsibility is communicating with a provider — and use this client to fire requests at Prism. If those requests are syntactically correct, and do not violate the constraints imposed by a given contract, Prism will respond as appropriate. If your application can handle the provided responses (i.e. deserialize the JSON responses), then your tests should be considered successful.

通常的想法是,在精心设计的应用程序中某种程度上遵循单一职责原则,您从代码库中挑选了一个客户端(该客户端的唯一职责是与提供者进行通信),并使用该客户端在棱镜。 如果这些请求在语法上是正确的,并且没有违反给定合同施加的限制,则Prism将酌情做出回应。 如果您的应用程序可以处理提供的响应(即反序列化JSON响应),那么您的测试应该被认为是成功的。

Image for post
Figure 1g: Consumer-side contract testing with Prism
图1g:使用Prism进行的消费者方合同测试

Network communication is bidirectional and, in the case of consumer-side contract tests, the successful handling of a response may require you to equip your thinking hat. If you’re working with a strongly-typed language such as Java, where response data is typically deserialized and mapped into runtime objects, and your objects appropriately annotated (e.g. @NotNull on required fields), you can assert with a high-level of confidence that you’re not violating a contract agreement if a response is deserialized successfully.

网络通信是双向的,在消费者方合同测试的情况下,成功处理响应可能需要您多加考虑。 如果您使用的是Java之类的强类型语言,通常将响应数据反序列化并映射到运行时对象中,并且对对象进行适当注释(例如,必填字段上的@NotNull),则可以使用如果成功反序列化了您的信心,您就不会违反合同协议。

So, what about languages like JavaScript, where JSON (the de-facto serialization standard on the internet) is the native format, and no deserialization is required? In JavaScript, it’s common for response data to be assigned to a variable for later evaluation, without implicit or explicitly checking the contents of a response. For example:

那么,像JavaScript这样的语言呢?其中JSON(互联网上的实际序列化标准)是本机格式,并且不需要反序列化吗? 在JavaScript中,通常将响应数据分配给变量以供以后评估,而无需隐式或显式检查响应的内容。 例如:

var responseData = axios.get();

var responseData = axios.get();

We may not use this response data immediately. What if our application expects responseData to contain a dog, but it contains a duck? Our application will blow up in unexpected ways. Assuming our providers are well-behaved, and don’t publish breaking changes to a contract (in the case of an enforced, enterprise-wide contract-testing strategy!), we’d like to find out if our application is violating a contract as fast as humanely possible. Runtime outages are very expensive.

我们可能不会立即使用此响应数据。 如果我们的应用程序期望responseData包含一只狗,但其中包含一只鸭子,该怎么办? 我们的应用程序将以意想不到的方式爆炸。 假设我们的提供者行为规范,并且不发布对合同的重大更改(在强制性的企业范围内的合同测试策略的情况下!),我们想了解我们的应用程序是否违反了合同尽快做到人道的 。 运行时中断非常昂贵。

Consumer-Side Contract Testing (with no deserialization)

消费者方合同测试(无反序列化)

Luckily, at Gamesys, we quickly realised that JSON Schema, a ‘specification vocabulary’ for JSON objects, had a high degree of parity with OpenAPI, with plans to have complete parity in the future. This meant that all we needed to do was, in our client-side clients, compare the response data against some schema, as defined by the provider contract. If it response doesn’t match, handle it as appropriate. Many may be wincing — shoehorning elements of Java-esque deserialization into our beautiful front-ends, and it might not ideal for your project. But, asides from the up-front technical toll of baking these schemas into our codebase, it cost very us very little, with high returns in the form of deployment confidence.

幸运的是,在Gamesys,我们很快意识到JSON Schema是JSON对象的“规范词汇”,它与OpenAPI具有高度的奇偶性,并计划在将来实现完全奇偶性。 这意味着我们需要做的是,在客户端客户端中,将响应数据与提供者合同定义的某种模式进行比较。 如果响应不匹配,请适当处理。 许多人可能会畏缩-将Java式反序列化的lization角元素融入我们美丽的前端中,对于您的项目而言,它可能不是理想的选择。 但是,从将这些模式烘焙到我们的代码库中的前期技术损失来看,这花费了我们很少的钱,并且以部署信心的形式获得了高回报。

Of course, performance is king on the client-side. We diligently performed performance tests and ascertained that, unless you’re running with a Nokia 3310 boasting an integrated Pentium chipset, performance is unlikely to be affected by schema validation. Nevertheless, a compromise could be to only run schema-validation during consumer-side contract tests — you’re unlikely to recover if providers decide to violate their end of the bargain at runtime.

当然,性能是客户端的决定性因素。 我们努力进行了性能测试,并确定,除非您使用的是拥有集成奔腾芯片组的诺基亚3310,否则性能不太可能受到架构验证的影响。 但是,折衷方案可能是仅在消费者方合同测试期间运行架构验证-如果提供者决定在运行时违反其议价范围,则您不太可能恢复。

Image for post
Figure 1h: Using JSON Schema to validate your responses in JavaScript
图1h:使用JSON模式验证JavaScript中的响应

What are the alternatives?

有哪些选择?

Historically, we’ve ran two types of integration tests at Gamesys:

从历史上看,我们在Gamesys上进行过两种类型的集成测试:

Classical Integration Tests: Spinning up an application, with real instances of their dependent applications and middleware, and running tests.

经典集成测试:启动应用程序,包括其依赖应用程序和中间件的真实实例,并运行测试。

Image for post
Figure 1i: Classical Integration Testing
图1i:经典集成测试

Fixture-based Integration Tests: Spinning up an application, with mocked instances of their dependent applications and middleware, and running tests.

基于夹具的集成测试:启动一个应用程序,包括其依赖的应用程序和中间件的模拟实例,并运行测试。

Image for post
Figure 1j: Fixture-based Integration Tests
图1j:基于夹具的集成测试

Both options are fatally flawed. Classical integration tests, as you will have likely experienced yourself, are beset by transient network issues, evolving dependencies and general slowness. Their brittle nature has been a focal point of developer ire over the years — resulting in frustration, reduced productivity and diminishing our ability to release frequently.

两种选择都存在致命缺陷。 您可能已经经历过的经典集成测试受瞬态网络问题,不断发展的依赖关系和总体缓慢性困扰。 多年来,它们的脆性一直是开发人员的关注焦点-导致挫败感,生产力下降并降低了我们频繁发布的能力。

Fixture-based integration tests, whilst certainly preferable, are subject to human error. To provide total confidence, they rely on developers diligently updating fixtures when APIs are changed. We’re in the business of automation so this approach seems, unsurprisingly, non-sensical when juxtaposed with superior testing options such as contract testing.

虽然基于夹具的集成测试固然更可取,但还是容易出错。 为了提供完全的信心,他们依赖开发人员在更改API时勤奋地更新固定装置。 我们从事自动化业务,因此,毫无疑问,这种方法与诸如合同测试之类的高级测试选项并列时,是不明智的。

Why not consumer-driven contract testing?

为什么不进行消费者驱动的合同测试?

It’s a question of trade-offs, and assessing appropriateness for our platform. A core tenet of consumer-side contract testing is that consumers dictate what they need from providers. Whilst it’s hard to disagree that, yes, the value in service providers is at the point of consumption, we feel a contract should represent a balanced and mutual agreement between a provider and her consumers. Ultimately, a provider is the owner and expert of their provided context — consumers and providers should negotiate on which data points are required for exposing. Consumer-driven contract testing represents a dangerous subversion of control.

这是一个权衡问题,需要评估我们平台的适用性。 消费者方合同测试的核心原则是,消费者决定提供者的需求。 尽管很难不同意,是的,服务提供商的价值在于消费点,但我们认为合同应该代表提供商与她的消费者之间的平衡且相互同意的协议 。 最终,提供者是其所提供上下文的所有者和专家-消费者和提供者应协商公开哪些数据点所需。 消费者驱动的合同测试代表着对控制的颠覆。

Pact, the market-leader for consumer-side contract testing, offers a powerful ecosystem. Unfortunately, this also means there is an element of architectural disruption and vendor lock-in associated with adoption. Tests are written in a Pact-specific DSL, and there is, of course, monetary cost associated with it. We’re not actively looking to discredit or discourage this approach, but to simply assess the pro and cons within the context of your enterprise.

Pact是消费者方合同测试的市场领导者,提供了强大的生态系统。 不幸的是,这也意味着与采用相关的架构破坏和供应商锁定的要素。 测试是在Pact专用DSL中编写的,当然会有与之相关的金钱成本。 我们并不是在积极寻求抹黑或劝阻这种方法,而只是在您的企业范围内评估优缺点。

Pitfalls

陷阱

Like all approaches and patterns that we choose to adopt, there are trade-offs to be made. Our chosen approach is low-cost and low-risk, in regards to any form of vendor lock-in. Perhaps arguable, but some may say our approach, once prototyped, is faster to bootstrap a given project with, as our approach relies on existing testing methodologies (e.g. plain old asserting) and libraries (such as JUnit).

像我们选择采用的所有方法和模式一样,需要权衡取舍。 对于任何形式的供应商锁定,我们选择的方法都是低成本和低风险的。 也许有争议,但有人说我们的方法一旦原型化,就可以更快地引导给定的项目,因为我们的方法依赖于现有的测试方法(例如普通的旧断言)和库(例如JUnit)。

However, Dredd and Prism may lack the sort of commercial long-term support that Pact offers. Both Dredd and Prism are open-source projects and, as such, rely on the goodwill of contributors to actively maintain. Open-source, of course, is a double-edged sword in this regard — we’re potentially reliant on volunteers pushing updates and fixes to these projects, but equally we’re capable of making pull requests ourselves, as necessary.

但是,Dredd和Prism可能缺乏Pact提供的那种商业长期支持。 Dredd和Prism都是开源项目,因此,依靠贡献者的善意来积极维护。 在这方面,开源当然是一把双刃剑–我们可能依赖志愿者为这些项目推送更新和修复,但是同样,我们能够根据需要自行提出请求。

It’s entirely possible that Dredd and Prism may one day be discontinued relics that require replacing. The good news, in response to this, is that they have limited responsibilities, and it’s unlikely that we’ll be left high-and-dry in the event of their demise. Other tools already exist, and new tools will almost certainly come into the fray in the coming years.

Dredd和Prism很有可能有一天可能会停产而需要更换。 对此的好消息是,他们的责任有限 ,一旦他们去世,我们不太可能一劳永逸。 其他工具已经存在,并且在未来几年中几乎肯定会出现新工具。

Conclusion

结论

Truth be told, we’re only just starting on our contract testing journey in Gamesys. We’ve definitely experienced bumps in the road — Dredd and Prism both have flaws but, for all intents and purposes, early indications suggest that this approach is much more reliable alternative to both classical integration tests, and fixture-based integration tests.

说实话,我们只是在Gamesys中进行合同测试之旅。 我们肯定已经经历了颠簸-Dredd和Prism都有缺陷,但是就所有意图和目的而言,早期迹象表明,这种方法对于传统的集成测试和基于夹具的集成测试都是可靠得多的替代方法。

We’ve yet to experience any flakiness running these tests — barring some network misconfiguration on our pipelines — and, when understood by developers, provide both improved documentation and a bigger sense of confidence when deploying to production.

在运行这些测试时,我们还没有遇到任何麻烦-禁止在管道上进行某些网络错误配置-并且,当开发人员理解时,在部署到生产环境时,既提供了改进的文档,又提供了更大的信心。

翻译自: https://medium.com/swlh/http-based-contract-testing-gamesys-8c8d0d9aeee8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值