51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 778|回复: 0
打印 上一主题 下一主题

微服务契约测试难点解析总结分享

[复制链接]
  • TA的每日心情
    擦汗
    2 小时前
  • 签到天数: 1047 天

    连续签到: 5 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2023-1-30 13:22:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    微服务体系测试的难点
      微服务中存在大量的进程间通信,通信方式可能是同步调用,也可能是使用消息组件进行异步通信。
      一对服务之间的交互称作两个服务的契约,比如,支付服务和账单服务之间就需要在通信用的消息结构体上达成一致,同理,网关和下游服务就需要在REST接口上达成一致,也因此开发人员需要保证自己负责的服务有稳定的API。
      要验证两个服务的交互,常见的方法就是运行两个服务,然后调用通信API,看看是否符合预期。但这往往会遇到集成的问题,并且会涉及到端到端,测试过程要尽可能避免端到端的测试。为此,契约测试需要由消费者来驱动。
      消费者驱动的契约测试
      假设现在有一个网关,有一个下游的用户服务,网关用来调用用户服务的查询用户信息的接口,为此需要编写测试来验证网关和用户服务能够正常交互。这个场景中,网关是消费者,用户服务是生产者,消费者驱动的契约测试就是它的生产者的集成测试,用来验证查询用户信息的接口是否符合网关的期望。
      那么在这个场景中如何才能证明生产者接口符合消费者期望呢?
      接口具备预期的HTTP请求方法(GET/POST/PUT/DELETE....)和路径
      带有预期的Request Header(假设存在)
      符合预期的Request Body(假设存在)
      返回了预期的Response Status/Body/Headers
      注意:契约测试不会涉及业务逻辑的测试。
      团队的协作方式如下图:

      契约测试组件一般是一些对应API请求的示例。
      缺少CDC的结果
      最常见的情况就是,原本在线上运行的好好的服务,在其他某个服务更新后出现了问题。
      而作为生产者的服务,需要修改接口的时候,不知道到底有哪些消费者在消费自己的接口,而且一旦开始通过调用链路寻找,很可能会牵扯出一大片的服务,导致不敢修改。
      Spring Cloud Contract的使用
      Spring-Cloud-Contract是Spring提供的一个契约测试框架,下面举个使用的例子.
      我是网关的开发者,我现在要为用户服务编写一个消费者契约测试,流程如下图。我编写的契约定义了网关如何与用户服务进行交互,用户服务的开发小组使用这些契约来测试用户服务,而我则用它们来测试网关。

      我编写一个或者多个契约,每个契约里面有且仅有一个网关可能发送给用户服务的HTTP请求,和一个预期的HTTP响应,我通过git Pull Request的方式将契约交付给用户服务的开发小组。
      用户服务的开发小组使用消费者契约测试用户服务,测试代码由Spring-Cloud-Contract从契约代码中生成。
      用户服务开发组将用于测试用户服务的契约发布到Maven仓库。
      我使用已发布契约为网关进行测试。
      因为我使用的是已经发布的契约来对网关进行测试,因此我可以确信它能够跟部署的用户服务一起协作。
      一个契约差不多长这样,我是用Groovy DSL写的,也可以用YAML来写。
    1. package contracts​import org.springframework.cloud.contract.spec.Contract​Contract.make {    request {        method 'POST'        urlPath('/user/getUser')    }    response {        status 200        body(                '''                {    "name": "Default User"}                '''        )        headers {            header('Content-Type', 'application/json')        }    }}
    复制代码


    install之后就会生成类似下面的代码,还有STUB的jar包(我这里生成的是client-a-server-1.0-SNAPSHOT-stubs.jar)等,具体可自行尝试。
    1. @SuppressWarnings("rawtypes")public class ContractVerifierTest extends ContractVerifierBase {​   [url=home.php?mod=space&uid=724]@test[/url]   public void validate_getUserInfo() throws Exception {      // given:         MockMvcRequestSpecification request = given();​​      // when:         ResponseOptions response = given().spec(request)               .post("/user/getUser");​      // then:         assertThat(response.statusCode()).isEqualTo(200);         assertThat(response.header("Content-Type")).isEqualTo("application/json");​      // and:         DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());         assertThatJson(parsedJson).field("['name']").isEqualTo("Default User");   }​}
    复制代码


    接下来就去消费者服务中编写测试调用的代码,在没有开启A服务的情况下,该测试方法一样可以执行成功(我在这边用的是CLASSPATH的方式引入stub包,实际生产中应用REMOTE,从远程Maven仓库下载)。
    1. @RunWith(SpringRunner.class)@SpringBootTest(classes = ClientB.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)@AutoConfigureMockMvc@AutoConfigureJsonTesters@Slf4j@AutoConfigureStubRunner(ids = {"org.example:client-a-server:1.0:stubs"}, stubsMode = StubRunnerProperties.StubsMode.CLASSPATH)public class ClientBTest {​    @Autowired    private ClientAFeignClient feignClient;​    @Test    public void testGetUser() {        ResponseEntity<UserDTO> dtoResponseEntity = feignClient.getUser();        log.info("Entity={}", dtoResponseEntity);    }}
    复制代码


    由于是Feign调用,因此还要加上配置。
    1. stubrunner.ids-to-service-ids.client-a-server=Client-A
    复制代码


    client-a-server是A服务的artifactId,Client-A是Feign-Client的名称。

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-15 11:21 , Processed in 0.063393 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表