合同测试简介 - Elizabeth Fiennes


合同测试是对部署或模拟服务端点的询问,以在部署之前获取信息或在测试中定义端点。

术语
服务消费者:向另一个组件发起HTTP请求的组件。
服务提供者:响应来自其他组件的HTTP请求的服务器。
< - 总的来说,请求和响应对称为交互 - >
服务广告:服务使用者通过嵌入或呈现机器可理解的Web服务描述来广告(提供)Web服务的方法。
合同文件:包含在使用者测试中定义的JSON序列化交互(请求和响应)。
模拟服务提供商:消费者测试用于模拟实际的服务提供者,这意味着可以运行类似集成的测试而无需实际的服务提供者。
服务协议:服务提供者在服务使用者提供特定请求后返回特定响应的承诺。
内部服务消费者:这是贵公司的另一个系统,它将您的服务用于身份验证,审计权限或数据等事务。
外部服务消费者:这是您公司外部的一方,它将您的服务作为其技术产品的一部分进行消费

怎么做?
由于合同测试是一种测试服务的方法,因此有三种方法:

  • 针对已部署的服务(URL)
  • 针对模拟服务(代码)
  • 针对待定服务(无)

部署是王道:对已部署的服务进行测试将是一种建议的方法,以确保在进行开发更改后,服务(例如API提供者和客户端)仍可以相互通信。

在针对模拟进行测试时,它可以替代某些昂贵/耗时/脆弱的集成测试。关于后一点,值得一提的是合同测试不能完全取代集成测试。

当针对尚待开发的API进行测试时,测试将全部失败,因为他们“指向”空气。在编写测试时,它们可以用作在测试驱动开发(TDD)中工作的团队的对话和设计决策的管道。在编写端点时,测试将通过并验证提供商的合同是否已经完成。

什么方式?
1.了解你的角色:
您是提供者还是消费者?您的消费是内部还是外部关注?如果您是提供者,则需要查看所有端点和内部集成。如果您是消费者(面向外部),您可能只是在查看您消耗的端点而不是每个端点。
并不是说我们没有具体的答案,只是我们需要大量的信息和背景才能给你正确的答案。

2. API文档
为了在部署的服务上或通过模拟进行合同测试,必须向所有各方提供合同。这是我们的API文档。根据组织的REST成熟度,其质量可能会有所不同。
符合REST Richardson成熟度模型(RMM)3级的API发布者将通过端点响应公开合同声明,无论是真实的还是模拟的。大多数提供商都在RMM Level 2上生成API,因此提供准确的文档更为重要。有许多工具可以自动生成针对已知标准定义的API的文档 - OpenAPI / Swagger,RAML或API Blueprint。

3.测试工具
在构建合同测试时,您可以使用许多工具,具体取决于您要进行的测试的范围和视角。PACT适用于内部提供商和以消费者为中心的测试。对于那些专注于消费者测试的人,建议使用Spring云合约Hoverfly是用Go编写的,具有对Java的本机支持,可以在JUnit测试中运行。Hoverfly可用于测试REST API以及测试微服务之间的调用。

4.定义您的测试范围
开发人员和测试人员很难解决的一件事是他们应该在测试过程中对功能的深入了解。这实际上取决于您希望从测试中获得的信息级别,它们所针对的信息以及您作为提供者或消费者的观点。以下是一些建议的范围,但它们并非详尽无遗,与一般的合同测试一样,您必须考虑哪种方法适合您以及您最好的工作环境。

一些测试 - 数据输入/部署的端点/顺序功能/改变数据状态
首先我们可以POST一个新的宠物:

POST "https://petstore.swagger.io/v2/pet" -H "accept: application/xml" -H "Content-Type: application/json" -d "{ \"id\": 0, \"category\": { \"id\": 0, \"name\": \"string\" }, \"name\": \"Dinosaur\", \"photoUrls\": [ \"string\" ], \"tags\": [ { \"id\": 0, \"name\": \"string\" } ], \"status\": \"available\"}"

根据上面POST请求中提供的数据,我们可以将其余的请求作为测试来排序,以获取预期的数据。快乐的狩猎!

PUT /pet //Updates the pet
GET /pet/findByStatus
//Finds Pets by status
GET /pet/findByTags
//Finds Pets by tags
GET /pet/{petId}
//Find pet by ID
POST /pet/{petId}
//Updates a pet in the store with form data
DELETE /pet/{petId}
//Deletes a pet
POST /pet/{petId}/uploadImage
//uploads an image

使用cURL对GET / Pets端点运行一些检查

**测试:**
获取一个不存在的端点,以确保服务可以干净地处理这个端点并提醒我们我们尝试使用的任何不正确的端点。(是的 - 这是一个顽皮的测试,因为Petstore没有将此响应称为支持)

请求(在cURL中):(
GET -I -X "https://petstore.swagger.io/v2/pet/
-I开关将确保cURL输出响应

响应:

HTTP/1.1 405 Method Not Allowed
Date: Thu, 20 Dec 2019 14:16:29 GMT
Access-Control-Allow-Origin: * 
Access-Control-Allow-Methods: GET, POST, DELETE, PUT 
Access-Control-Allow-Headers: A, B, C, D 
Content-Type: application/json 
Connection: close 
Server: Betty Boo 

**测试:**
为存在的端点获取生成的STATUS端点
请求: 

C:\DEV\cURL\bin>curl -I -X GET "https://petstore.swagger.io/v2/pet/findByStatus?status=BLAH"

响应: 

HTTP/1.1 200 OK 
Date: Thu, 20 Dec 2019 14:16:29 GMT
Access-Control-Allow-Origin: * 
Access-Control-Allow-Methods: GET, POST, DELETE, PUT 
Access-Control-Allow-Headers: A, B, C, D 
Content-Type: application/json 
Connection: close 
Server: Betty Boo 

模拟端点/ TDD - 经典或伦敦学校
关于TDD的另一种方式已经写了很多,而这里没有重复。它还伴随着一大堆争论,采取TDD还是合同测试讨论。对于想要更详细地研究这个内容的人,如果您想研究查看基于角色的测试重点的算法经验的方式和方法,我提供了链接以提供更多上下文。 伦敦学校芝加哥学院

从测试中获得了哪些信息?
对已部署服务的良好合同测试将告诉您是否:
*正在测试的API了解其所做的请求
*正在测试的API可以发送预期的响应
*端点可访问
*提供者和消费者有工作连接,如果您正在测试已部署的服务
*如果预期的集成已到位

同样可以说对于模拟和TDD合同测试,只有在部署的服务版本对应于模拟和测试时。因此,应使用模拟和TDD测试来推动设计决策,而不是提供信息。

合同测试可以测试什么?
任何研究合同测试的人都会在合同测试的使用和界限上找到大量相互矛盾的信息。我不打算明确说明是否进行的合同测试以及在什么情况下适合。

如果您正在为测试模拟API端点,那么合同测试将不会向您提供有关最终部署的端点的任何信息。如果要查找端点配置问题或误用类等潜在问题,则需要针对已部署的服务运行一些独立或集成测试。

如果您有关于数据的规则,例如最大和最小边界,格式和大小。除非您正在测试部署的服务版本,否则合同测试不会以有效的方式测试这些规则。

无法测试的指标
协议是被测服务提供的API规范。因此,无法测试该规范中未描述的任何功能。这包括:
*基于客户的工作流程
*一些UI功能
*服务水平协议(SLA)
*故障转移计划
*压力下的性能
*部署环境的适用性

如果您的内部或外部消费者希望能够以合同中未记录的方式使用您的服务,那么您们都无法对此进行测试,除非它被编写为期望失败的未来状态测试在第一次运行。这就是为什么在测试任何请求/响应对开始之前确保共享是很重要的原因。 

合同测试不可以测试什么?
基于合同的单元测试仅检查API端点连接是否处于活动状态并正常运行(响应)。合同测试确保服务按照协议的标准做出响应。为了提供一个协议的实例,我将使用Swagger Petstore API端点 -
根据规范,Petstore API将返回200以查询要求其查找状态为“可用”,“待定”或“已售出”的任何查询。对于其他任何东西,它将返回400.有关http请求和响应的更多信息 - 请查看此处。我们来测试这个协议:
如果我发送了请求: 
GET https://petstore.swagger.io/v2/pet/findByStatus?status=available
=>我应该收到200响应,因为这是一个有效的请求。Petstore支持“可用”状态

GET https://petstore.swagger.io/v2/pet/findBySize
=>我应该得到400响应,因为这是一个无效的请求。由于Petstore`不支持“findbysize”选项

您可以使用Swagger'Try it out'功能,cURL或Postman来玩Petstore请求。请记住,swagger.io接口仅测试快乐路径,因此您需要使用其他工具之一发送请求语法以调用400/500类型响应。

如何将合同测试纳入我的整体方法?
尽量不要将合同测试视为单元测试,独立API功能测试或集成测试的替代,它更像是一个免费的测试流。只要您可以避免在测试和测试的不同阶段重复测试尽可能接近代码。

1.独立的API功能测试 
当我能够推荐合同测试时,我最常问的问题是“oooh可以替代独立的API测试吗?答案是肯定的......没有。如果您在可以访问的环境中测试已部署的API,则合同测试肯定会验证API是否按预期工作。但是,如果您正在模拟端点,作为内部或外部使用者,您无法替换此测试,尤其是如果测试结果有可能导致合同发生变化,那么部署与模拟的内容不同。

2.集成测试
合同测试无法找到非逻辑缺陷,例如负面测试暴露的异常,也无法检测数据库条目或连接的配置问题。 

这是集成测试(或端到端测试)发挥作用的地方。如果您已经完成了合同测试(并且您应该尽可能早地在开发周期中进行测试),那么您可以确保您的服务端点正在处理请求并按预期进行响应但是如果计划的部署将会有效与它的集成服务/数据库/认证系统等。 还值得记住的是,合同测试会告诉您存在中断但不一定中断的地方与独立测试或集成测试相同。 

用例
用例1 - 没有开发服务的内部消费者

我是TeamA(消费者)的测试人员。TeamB(提供商)将开发一种新服务,我测试的组件必须与之集成。我想写一些测试来确保两者之间的集成。与TeamA和TeamB的开发人员合作,我将针对新服务针对模拟端点编写一些合同测试,以确保在我的组件广告端点时,它们将按预期进行集成。 

用例2 - 具有开发服务的外部消费者
我是一名网络开发人员,在我的漫画书店店面消费漫威漫画API。我想确保对Marvel API的任何更改都不会破坏我所宣传的服务,因为这会破坏我的目标网页的几个部分。我将为我的测试人员提供API规范,以便我们可以模拟API。由此产生的合同测试将帮助我们验证在对服务URL或单个端点进行任何更改后,我们期望Marvel作为我们的提供商的所有协议仍然存在。

用例3 - 生产中具有端点的内部消费者​​​​​​​
我们是一个Scrum团队。我们已经构建了一个新组件来替换已经在生产中的组件。组件使用的API也在生产中,但除了内部开发团队之外的任何人都不能使用。我们不希望为这一个变化构建一个完整的回归环境,因为我们有能力在生产中进行测试。但是,我们确实希望保证部署的组件可以在部署到实时之前访问它所宣传的所有端点。我们将模拟现有的端点,并通过合同测试确保我们的新组件可以使用它们。 

用例4 - 做出哪些技术设计决策
我们是一个Scrum团队。我们打算为基于云的容器构建和部署新API。但是,团队内部是否存在使用REST或SOAP服务的争论。双方都有好处使用。我们决定对这两者进行POC,并了解API在功能级别上的响应能力。这意味着我们将监视对API调用的响应的SLA时间。如果从第一次调用到响应的响应时间超过100毫秒,我们编写测试并输入一个声明为失败的断言的子句。通过这种方式,我们将了解应用程序中的快乐路径,哪些功能在相同环境中具有最高性能,但具有不同的服务设计。 

​​​​​​​