• 契约测试笔记


    契约测试笔记

    字面上意思是签订一份双方甚至多方的的一份约定/合同,最早可以出现在买卖,抵押等活动,如果一方违背了这份契约,那就得付出相应的处罚(锅你来背)

    在软件开发中,测试被测系统崩溃,产品需求变更,项目进度加快等我们不可抗的因素导致我们测试被拖慢,开发压榨测试时间,测试时间不够(这也是时常发生的问题),后来有一种新的思想--测试替身,不再调用还未开发的api接口,使用测试替身模拟接口的实现(mock就是一种测试替身的手段),但是mock完全不能代替开发写的api,如开发变更了接口,那我们的mock就白写了,而且我们还不知道开发改了,只有调用报错了才知道,那么这样想只要真实api和mock返回数据是一样的就可以解决这个问题吗?这其实是无法保证的

    由此诞生了消费者驱动的契约测试(Consumer-Driven Contracts,简称CDC)

    一种是根据消费者驱动契约,还有一种是生产者驱动契约,我们主要使用的方式是消费者驱动契约的方式,原因是能够验证服务是否满足我们消费方的期待,本质是从利益者和使用者的角度,体验,目标作为出发点,满足我们一些业务的价值实现.当然生产者驱动契约模式是有的,但我更喜欢两者来维护这份契约,而不是一方提出

    为什么要做契约测试?

    可能说不上为什么要去做,可能是如今微服务架构复杂,我们平时看百度一下的页面,不单单是测前端哪个按钮能点不能点,背后大量的api调用,大量的微服务嵌套关系,这是如今各种高内聚低耦合的服务架构发展,而且所有开发人员来自不同城市,不同地区,不得不指定契约来做这些事情,如不同区域调用同一份契约,一个接口从1.0变更为2.0,我们需要约定俗成,2.0必须要覆盖1.0的测试需求,而且保证所有的返回数据都能测试到,那样我们说契约测试才会有效果

    所以说白了来说就是我们消费者提供一个json/yml格式的文件,再由生产者来实现契约,这样之后就能够实现我们之间说的两者之间的同步问题,当版本迭代或者是接口更新时,新版本中的契约同步是通过旧版接口测试新版的接口,当测试通过后再更新新版本的契约,上线时切换到新接口中

    契约测试的不适合场景

      公共api服务

      性能测试

      消费者和生产者没有沟通渠道

    契约测试工具

    Pact

      基于json文件格式

    Swagger

      基于yml文件格式

    这里我来着重学swagger,首先使用方便,对python-django,flask都有扩展,其次它可以根据注释来生成文档,比如github上一些api接口文档开发人员不会开发完再回头重新写一遍文档,这种都是用工具自动就可以生成的,当然也可以用yml文档生成,安装生成也很简单,只要node.js和相关扩展就行了,当然如果不急着做契约测试,做出一份漂亮的接口文档给开发看,心里也是乐滋滋的

    # pip install flasgger
    
    # 简易示例
    import random
    from flask import Flask, jsonify, request
    from flasgger import Swagger, swag_from
    import json
    
    app = Flask(__name__)
    Swagger(app)
    
    #swagger为本地文档yml文件的情况
    @app.route('/aaa/<string:language>/', methods=['GET'])
    @swag_from("api_get.yml")
    def aaa(language):
        language = language.lower().strip()
        features = [
            "awesome", "great", "dynamic",
            "simple", "powerful", "amazing",
            "perfect", "beauty", "lovely"
        ]
        size = int(request.args.get('size', 1))
        if language in ['php', 'vb', 'visualbasic', 'actionscript']:
            return "An error occurred, invalid language for awesomeness", 500
        return jsonify(
            language=language,
            features=random.sample(features, size)
        )
    
    
    #多methods的情况
    @app.route('/test/', methods=['POST', 'GET', ])
    def test():
        '''
        这里写接口简单描述信息
        这里写详细的接口信息xxxxxxxxxxxxxxxxxx
        xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        ---
        tags:
          - Awesomeness Language API
        parameters:
          - name: username
            in: path
            type: string
            required: true
            description: 这里填入参描述  姓名
          - name: password
            in: query
            type: int
            description: 这里填入参描述  密码
        responses:
          500:
            description: 这里填描述 500了
          200:
            description: A ok了
        '''
        if request.method.lower() == 'get':
            res = {'status': '这是get请求'}
        elif request.method.lower() == 'post':
            res = {'status': '这是post请求'}
        else:
            res = {'status': '不支持该方法'}
        return json.dumps(res,ensure_ascii=False,indent=4)
    
    
    #带参数的get请求
    @app.route('/api/<string:language>/', methods=['GET'])
    def index(language):
        """
        This is the language awesomeness API
        Call this api passing a language name and get back its features
        ---
        tags:
          - Awesomeness Language API
        parameters:
          - name: language
            in: path
            type: string
            required: true
            description: The language name
          - name: size
            in: query
            type: integer
            description: size of awesomeness
        responses:
          500:
            description: Error The language is not awesome!
          200:
            description: A language with its awesomeness
            schema:
              id: awesome
              properties:
                language:
                  type: string
                  description: The language name
                  default: Lua
                features:
                  type: array
                  description: The awesomeness list
                  items:
                    type: string
                  default: ["perfect", "simple", "lovely"]
    
        """
        language = language.lower().strip()
        features = [
            "awesome", "great", "dynamic",
            "simple", "powerful", "amazing",
            "perfect", "beauty", "lovely"
        ]
        size = int(request.args.get('size', 1))
        if language in ['php', 'vb', 'visualbasic', 'actionscript']:
            return "An error occurred, invalid language for awesomeness", 500
        return jsonify(
            language=language,
            features=random.sample(features, size)
        )
    
    app.run(debug=True)

    启动flask后,访问http://127.0.0.1:5000/apidocs

    我们可以看到这就是我们在测试服务器的用文档制定的契约,我们定义了3个接口,其中多出来的一个是post请求,有点restful风格,这里的接口都是可以点击展开的,点开后如下图

    版权声明:本文原创发表于 博客园,作者为 RainBol 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。

  • 相关阅读:
    window.open和window.opener
    dict对象与QueryDict
    BeautifulSoup的一些方法
    ORM分组与聚合
    python-orm
    开发工具IDEA环境安装配置
    Greenplum介绍-table
    对package.json的理解和学习
    javaScript 的 map() reduce() foreach() filter()
    JSON的序列化和反序列化eval()和parse()方法以及stringfy()方法
  • 原文地址:https://www.cnblogs.com/RainBol/p/12448844.html
Copyright © 2020-2023  润新知