jax-rs jax-ws

自从我们谈论测试和应用有效的TDD做法以来,已经有一段时间了,特别是与REST(ful) Web服务和API有关的做法。 但是,这个主题永远都不应忘记,特别是在每个人都在做微服务的世界中,无论它意味着什么,暗示或采取什么措施。

公平地说,基于微服务的体系结构在很多领域大放异彩 ,使组织可以更快地移动和创新。 但是如果没有适当的纪律,这也会使我们的系统变得脆弱,因为它们变得非常松散。 在今天的帖子中,我们将讨论基于合同的测试和消费者驱动的合同,这是一种实用且可靠的技术,可确保我们的微服务兑现其承诺。

那么, 基于合同的测试如何工作? 简而言之,它是一种非常简单的技术,并遵循以下步骤:

  • 提供商(例如服务A )发布其联系人(或规范),则该实现可能在此阶段不可用
  • 消费者(例如服务B )遵循此合同(或规范)以实现与服务A的对话
  • 此外,消费者引入了一个测试套件,以验证其对服务A合同履行的期望

对于SOAP Web服务和API,事情很明显,因为以WSDL文件的形式存在显式契约。 但是在使用REST(ful) API的情况下,有很多不同的选择( WADL , RAML , Swagger …),并且仍然没有达成一致。 听起来可能很复杂,但请不要沮丧,因为Pact即将解救!

Pact是一系列框架,用于支持消费者驱动的合同测试。 有许多语言绑定和实现可用,包括JVM, JVM Pact和Scala-Pact 。 为了发展这种多语言生态系统, Pact还包括一个专用规范 ,以提供不同实现之间的互操作性。

太好了, Pact就在这里,阶段已经准备就绪,我们准备好迎接一些真实的代码片段。 让我们假设我们正在使用出色的Apache CXF和JAX-RS 2.0规范开发用于管理人员的REST(ful) Web API。 为简单起见,我们将仅介绍两个端点:

  • POST / people / v1创建新的人
  • GET / people / v1?email = <email>通过电子邮件地址查找人

从本质上讲,我们可能不会打扰他们,而只是将我们的服务合同中的这些最小部分传达给每个人,因此,让消费者自己解决这个问题(事实上, Pact支持这种情况)。 但是可以肯定的是,我们不是那样的,我们确实在乎,并且想全面地记录我们的API,可能我们已经熟悉Swagger了 。 这样,这就是我们的PeopleRestService

@Api(value = "Manage people")
@Path("/people/v1")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class PeopleRestService {@GET@ApiOperation(value = "Find person by e-mail", notes = "Find person by e-mail", response = Person.class)@ApiResponses({@ApiResponse(code = 404, message = "Person with such e-mail doesn't exists", response = GenericError.class)})public Response findPerson(@ApiParam(value = "E-Mail address to lookup for", required = true) @QueryParam("email") final String email) {// implementation here}@POST@ApiOperation(value = "Create new person", notes = "Create new person", response = Person.class)@ApiResponses({@ApiResponse(code = 201, message = "Person created successfully", response = Person.class),@ApiResponse(code = 409, message = "Person with such e-mail already exists", response = GenericError.class)})public Response addPerson(@Context UriInfo uriInfo, @ApiParam(required = true) PersonUpdate person) {// implementation here}
}

目前,实现细节并不重要,但是让我们看一下GenericErrorPersonUpdatePerson类,因为它们是我们服务合同不可分割的一部分。

@ApiModel(description = "Generic error representation")
public class GenericError {@ApiModelProperty(value = "Error message", required = true)private String message;
}@ApiModel(description = "Person resource representation")
public class PersonUpdate {@ApiModelProperty(value = "Person's first name", required = true) private String email;@ApiModelProperty(value = "Person's e-mail address", required = true) private String firstName;@ApiModelProperty(value = "Person's last name", required = true) private String lastName;@ApiModelProperty(value = "Person's age", required = true) private int age;
}@ApiModel(description = "Person resource representation")
public class Person extends PersonUpdate {@ApiModelProperty(value = "Person's identifier", required = true) private String id;
}

优秀的! 一旦我们有了Swagger批注并且打开了Apache CXF Swagger集成 ,我们就可以生成swagger.json规范文件,将其置于Swagger UI中并分发给每个合作伙伴或感兴趣的消费者。

如果我们可以将此Swagger规范与Pact框架实现一起用作服务合同,那就太好了。 感谢Atlassian ,我们当然可以使用swagger-request-validator来做到这一点, swagger-request-validator是一个用于根据Swagger / OpenAPI规范验证HTTP请求/响应的库,该库也很好地与Pact JVM集成在一起。

太好了,现在让我们从提供商转向消费者,尝试找出掌握此类Swagger规范可以做什么。 事实证明,我们可以做很多事情。 例如,让我们看一下创建新人员的POST操作。 作为客户(或消费者),我们可以用以下形式表达我们的期望:与请求一起提交有效的有效载荷,我们期望提供者返回HTTP状态代码201 ,并且响应有效载荷应该包含一个新的人。分配的标识符。 实际上,将此语句转换为Pact JVM断言非常简单。

@Pact(provider = PROVIDER_ID, consumer = CONSUMER_ID)
public PactFragment addPerson(PactDslWithProvider builder) {return builder.uponReceiving("POST new person").method("POST").path("/services/people/v1").body(new PactDslJsonBody().stringType("email").stringType("firstName").stringType("lastName").numberType("age")).willRespondWith().status(201).matchHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).body(new PactDslJsonBody().uuid("id").stringType("email").stringType("firstName").stringType("lastName").numberType("age")).toFragment();
}

为了触发合同验证过程,我们将使用很棒的JUnit和非常流行的REST保证框架。 但是在此之前,让我们从上面的代码片段中阐明什么是PROVIDER_IDCONSUMER_ID 。 如您所料, PROVIDER_ID是合同规范的参考。 为简单起见,我们将从运行PeopleRestService端点获取Swagger规范,幸运的是, Spring Boot测试改进使此任务变得轻而易举。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = PeopleRestConfiguration.class)
public class PeopleRestContractTest {private static final String PROVIDER_ID = "People Rest Service";private static final String CONSUMER_ID = "People Rest Service Consumer";private ValidatedPactProviderRule provider;@Value("${local.server.port}")private int port;@Rulepublic ValidatedPactProviderRule getValidatedPactProviderRule() {if (provider == null) {provider = new ValidatedPactProviderRule("http://localhost:" + port + "/services/swagger.json", null, PROVIDER_ID, this);}return provider;}
}

CONSUMER_ID只是识别消费者的一种方式,对此不多说。 这样,我们准备完成第一个测试用例:

@Test
@PactVerification(value = PROVIDER_ID, fragment = "addPerson")
public void testAddPerson() {given().contentType(ContentType.JSON).body(new PersonUpdate("tom@smith.com", "Tom", "Smith", 60)).post(provider.getConfig().url() + "/services/people/v1");
}

太棒了! 如此简单,请注意@PactVerification批注的存在,在这里我们通过名称引用了适当的验证片段,在这种情况下,它指出了我们之前介绍的addPerson方法。

很好,但是...有什么意义呢? 很高兴您提出这样的要求,因为从现在开始,合同中可能无法向后兼容的任何变更都将破坏我们的测试用例。 例如,如果提供程序决定从响应有效负载中删除id属性,则测试用例将失败。 重命名请求有效负载属性,不可以,测试用例将再次失败。 添加新的路径参数? 运气不好,测试用例不能通过。 您可能会走得更远,即使每次向后兼容(即使使用向后兼容swagger-validator.properties进行微调),每次合同更改也会失败。

validation.response=ERROR
validation.response.body.missing=ERROR

没有一个很好的主意,但是如果您需要它,它仍然在那里。 同样,让我们​​从成功的场景开始,为要寻找的人添加一些其他的GET端点测试用例,例如:

@Pact(provider = PROVIDER_ID, consumer = CONSUMER_ID)
public PactFragment findPerson(PactDslWithProvider builder) {return builder.uponReceiving("GET find person").method("GET").path("/services/people/v1").query("email=tom@smith.com").willRespondWith().status(200).matchHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).body(new PactDslJsonBody().uuid("id").stringType("email").stringType("firstName").stringType("lastName").numberType("age")).toFragment();
}@Test
@PactVerification(value = PROVIDER_ID, fragment = "findPerson")
public void testFindPerson() {given().contentType(ContentType.JSON).queryParam("email", "tom@smith.com").get(provider.getConfig().url() + "/services/people/v1");
}

请注意,这里我们引入了使用query(“ email=tom@smith.com”)断言进行查询字符串验证。 遵循可能的结果,让我们还介绍一下不成功的情况,即人员不存在,并且我们期望返回一些错误以及404状态代码,例如:

@Pact(provider = PROVIDER_ID, consumer = CONSUMER_ID)
public PactFragment findNonExistingPerson(PactDslWithProvider builder) {return builder.uponReceiving("GET find non-existing person").method("GET").path("/services/people/v1").query("email=tom@smith.com").willRespondWith().status(404).matchHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).body(new PactDslJsonBody().stringType("message")).toFragment();
}@Test
@PactVerification(value = PROVIDER_ID, fragment = "findNonExistingPerson")
public void testFindPersonWhichDoesNotExist() {given().contentType(ContentType.JSON).queryParam("email", "tom@smith.com").get(provider.getConfig().url() + "/services/people/v1");
}

真正出色,可维护,可理解且非侵入性的方法,可解决诸如基于合同的测试和由消费者驱动的合同之类的复杂而重要的问题。 希望这种有点新的测试技术可以帮助您在开发阶段捕获更多问题,从而避免它们有机会泄漏到生产中。

感谢Swagger,我们能够采取一些捷径,但是如果您没有这么奢侈的话, Pact会提供相当丰富的规范,非常欢迎您学习和使用。 无论如何, Pact JVM可以在帮助您编写小型而简洁的测试用例方面做得非常出色。

完整的项目资源可在Github上找到 。

翻译自: https://www.javacodegeeks.com/2016/11/keep-promises-contract-based-testing-jax-rs-apis.html

jax-rs jax-ws

jax-rs jax-ws_信守承诺:针对JAX-RS API的基于合同的测试相关推荐

  1. 信守承诺:JAX-RS API的基于合同的测试

    自从我们谈论测试和应用有效的TDD实践以来已经有一段时间了,特别是与REST(ful) Web服务和API相关的实践. 但是,这个主题永远都不应忘记,特别是在每个人都在做微服务的世界中,无论它意味着什 ...

  2. 学会针对永洪API接口的性能测试,工作效率提升百倍

    性能测试是指针对产品的业务场景,通过设计场景和压力,对产品进行高并发量.大数据量的测试,主要目的是为了确定产品在高并发情况下的各项指标:平均处理时间,QPS,网络IO,磁盘读写等.永洪BI具有高耦合性 ...

  3. 中国直接针对消费者的疾病风险和健康DNA测试行业市场供需与战略研究报告

    这些测试的结果估计您罹患几种常见疾病(如腹腔疾病,帕金森氏病和阿尔茨海默氏病)的遗传风险. 一些公司还将人的携带者身份包括较不常见的疾病,包括囊性纤维化和镰状细胞病. 携带者是指具有一个基因突变的一个 ...

  4. 华为maters保时捷鸿蒙,【华为MateRS保时捷版评测】编辑分享:妙用华为Mate RS保时捷之三摄_华为 Mate RS保时捷版_手机评测-中关村在线...

    [中关村在线]华为MateRS保时捷版评测:之前提到华为Mate RS保时捷设计外观(<华为Mate RS保时捷设计长测 一眼定情>)的时候提到过,它后面采用的是三枚摄像头,这是相比之前的 ...

  5. 社交媒体平台api接口功能_针对社交媒体API的新Java规范请求

    社交媒体平台api接口功能 针对Java 7的最新Java规范请求已经浮出水面,提出了一种用于访问私有和公共社交信息网络的API,范围从Facebook和Twitter到企业和机构内的网络. 社交媒体 ...

  6. 够迫履门夹钾灼敛墒套谮姑韩立对墨大夫一年后是否真的信守承诺,很是怀疑,若真是像对方所说

    后,對他猛下毒手韓立對墨大夫一年后是否真的信守承諾,很是懷疑,若真是像對方所說的這么簡單,倒是好辦了,他完全沒有對抗的必要,.但就怕對方隱瞞了對他不利的部分,到時翻臉下了毒手,他若不做絲毫準備,豈不連 ...

  7. 针对访问控制列表ACL 与 基于角色的访问控制RBAC进行简单介绍

    2019独角兽企业重金招聘Python工程师标准>>> 访问控制列表(Access Control List,ACL) ACL是最早也是最基本的一种访问控制机制,它的原理非常简单:每 ...

  8. 针对校园某服务器的一次渗透测试

    发现学校其中一台服务器可能(绝对)存在漏洞.我想(绝对)能拿下它; 而且渗透测试的过程很有趣,便将其记录下来. 0x00 前期交互及信息收集 由于是对内网直接进行大扫描,所以直接判断这不仅是一个Web ...

  9. java excel api及详细教程_针对Java Excel API及详细教程

    时在java开发中会操作excel表格,其实操作起来也特别简单.这里把前期操作步骤说一下,本文会简单的介绍一个开放源码项目:Java Excel Api,使用它大家就可以方便的操作Excel文件了. ...

最新文章

  1. LIVE 预告 | CVPR 2021 预讲 · 悉尼科技大学ReLER实验室专场
  2. C++ 把输出结果写入到文件中
  3. dialog element 删掉标题_ElementUI 销毁Dialog数据(简单粗暴)
  4. phalcon: Profiling分析 profilter / Plugin结合,dispatcher调度控制器 监听sql执行日志
  5. 【英语学习】【医学】Unit 09 The Respiratory System
  6. 【Kafka】Kafka事务是怎么实现的
  7. mysql多字段分库分表基因码_一文学会常用 MySQL 分库分表方案
  8. Microsoft.AlphaImageLoader滤镜解说
  9. (13)数据结构-先序中序还原二叉树
  10. 笠翁对韵(全卷,珍藏版附注释)
  11. jieba分词原理 ‖ 关键词抽取
  12. 读《亿级用户下的新浪微博平台架构》有感
  13. css 文本超出显示省略号不起作用
  14. iOS安全防护---越狱检测、二次打包检测、反调试
  15. SIGIR2020推荐系统论文聚焦
  16. 解密Uber自动驾驶系统,警方披露撞人案细节
  17. Linux驱动开发之RGB565转RGB888
  18. 极客日报:三星嘲讽iPhone13:120Hz高刷我们早用上了;华为撤回对OPPO欧洲专利的异议;淘宝搜索崩了登上热搜
  19. r 语言ylim = c(0 1),小白R语言数据可视化进阶练习一
  20. 铁电存储器FRAM与其他内存的比较

热门文章

  1. 【李超树】李超线段树维护凸包(凸壳) (例题:blue mary开公司+线段游戏+ZZH的旅行)
  2. P4548-[CTSC2006]歌唱王国【概率生成函数,KMP】
  3. P4292-[WC2010]重建计划【长链剖分,线段树,0/1分数规划】
  4. CSPNOIP2020总结
  5. P4083-[USACO17DEC]A Pie for a Pie G【线段树,最短路】
  6. P1943-LocalMaxima_NOI导刊2009提高(1)【数论】
  7. ssl提高组周一备考赛【2018.10.29】
  8. P1038-神经网络【拓扑排序】
  9. P3811-[模板]乘法逆元【线性求逆元】
  10. 【主席树】可持久化数组(金牌导航 可持久化数据结构-3)