• 主贴那个, 8 楼的还有问题,主贴上的是最终版, 不知道怎么删 8 楼

  • 代码写出来了,测试了一下,应该是没有 bug 了,
    在这里,你看看有没有 问题,

    import json
    import random
    import time
    
    from pactverify.matchers import Matcher, Like, EachLike, Enum, Term, PactVerify
    from pactverify.matchers import PactJsonVerify
    from pactverify.utils import generate_pact_json_by_response
    from xeger import Xeger
    str_xeger = Xeger(limit=10)
    
    def generateChr():
        #gbk2312对字符的编码采用两个字节相组合, 第一个字节的范围是0xB0 - 0xF7, 第二个字节的范围是0xA1 - 0xFE.在head区号为55的那一块最后5个汉字是乱码,为了方便缩减下范围
        temp='{0:x} {1:x}'.format(random.randint(0xb0, 0xf7), random.randint(0xa1, 0xf9) )
        chr=bytes.fromhex(temp).decode('gb2312')
        return chr
    
    def make_data(data_type):
        if isinstance(data_type, int):
            old_body = int(str_xeger.xeger(r'[0-9]{%d}' % random.randint(1, 10)))
        elif isinstance(data_type, str):
            old_body = str_xeger.xeger(r'[a-zA-Z0-9%s]{%d}' %(generateChr(),random.randint(1, 10)) )
        elif isinstance(data_type, float):
            old_body = float(str_xeger.xeger(
                r'[0-9]{%d}' % random.randint(1, 5)) + '.' + str_xeger.xeger(
                r'[0-9]{%d}' % random.randint(1, 10)))
        else:
            old_body =data_type
        return old_body
    
    def pact_like(dic_json,dic,pact_type=False):
        '''
        校验规则:类型匹配
        :param dic_json:
        :param dic:
        :return:
        '''
        if isinstance(dic_json, dict):
            for key in dic_json:
                if isinstance(dic_json[key], dict):  # 如果dic_json[key]依旧是字典类型
                    for key_down in dic_json[key]:
                        if key_down == '$Like':
                            if   isinstance(dic_json[key][key_down], dict) and '$values' in dic_json[key][key_down].keys()    :
                                dic_json[key] = make_data(dic_json[key][key_down]['$values'])
                            else:
                                dic_json[key] = make_data(dic_json[key][key_down])
                            pact_like(dic_json, dic,pact_type=pact_type)
                        else:
                            dic[key] = {}
                            pact_like(dic_json[key], dic[key],pact_type=pact_type)
                elif isinstance(dic_json[key], (list, tuple)):
                    new_key = []
                    for key_down in dic_json[key]:
                        new_dic={}
                        pact_like(key_down, new_dic,pact_type=pact_type)
                        if new_dic:
                            new_key.append(new_dic)
                        else:
                            new_key.append(key_down)
                    dic[key]=new_key
                else:
                    if pact_type:
                        if key not in  ['$Matcher' ,'iterate_list','key_missable','nullable','example','$Enum' ]     :
                            dic[key]  = make_data(dic_json[key])
                        else:
                            dic[key] = dic_json[key]
                    else:
                            dic[key] = dic_json[key]
    
        return  dic
    
    def pact_EachLike(dic_json,dic):
        '''
        校验规则:数组类型匹配
            EachLike 下的项目 如果没有特定,都按LIke实现
        :param dic_json:
        :param dic:
        :return:
        '''
        if isinstance(dic_json, dict):
            for key in dic_json:
                if isinstance(dic_json[key], dict):
                    if key =='$EachLike':
                        dic=[dic_json[key]]
                        return dic
                    else:
                        for key_down in dic_json[key]:
                            if key_down == '$EachLike':
                                ruest_EachLike = []
                                minimum = 1
                                if    isinstance( dic_json[key][key_down], dict) and   '$values' in dic_json[key][key_down].keys():
                                   #判断是否带参数
                                   if '$params' in  dic_json[key][key_down].keys():
                                       if 'minimum' in dic_json[key][key_down]['$params'].keys():
                                           minimum=dic_json[key][key_down]['$params']['minimum']
                                   old_body = dic_json[key][key_down]['$values']
                                   for x in range(random.randint(minimum,10)):
                                        if isinstance(dic_json[key][key_down]['$values'],dict):
                                            EachLike_dic = {}
                                            pact_like(old_body, EachLike_dic, pact_type=True,)
                                        else:
                                            EachLike_dic=make_data(dic_json[key][key_down]['$values'])
                                        ruest_EachLike.append(EachLike_dic)
                                   dic_json[key]=ruest_EachLike
                                else:
                                    ruest_EachLike=[]
                                    for x in  range(random.randint(minimum,10)):
                                        if isinstance(dic_json[key][key_down], dict):
                                            EachLike_dic = {}
                                            old_body = dic_json[key][key_down]
                                            pact_like(old_body, EachLike_dic, pact_type=True, )
                                            old_body=EachLike_dic
                                        else:
                                            old_body= make_data(dic_json[key][key_down])
                                        ruest_EachLike.append(old_body)
                                    dic_json[key]=ruest_EachLike
    
                                pact_EachLike(dic_json, dic,)
                            else:
                                dic[key] = {}
                                pact_EachLike(dic_json[key], dic[key],)
    
                elif isinstance(dic_json[key], (list, tuple)):
                    new_key = []
                    for key_down in dic_json[key]:
                        new_dic={}
                        retun_dic=pact_EachLike(key_down, new_dic,)
                        if new_dic:
                            new_key.append(new_dic)
                        elif retun_dic:
                            new_key.append(retun_dic)
                        else:
                            new_key.append(key_down)
                    dic[key]=new_key
    
                else:
                    dic[key] = dic_json[key]
    
    def pact_Matcher(dic_json,dic):
        '''
        校验规则:值匹配 不存在随机
        :param dic_json:
        :param dic:
        :return:
        '''
        if isinstance(dic_json, dict):
            for key in dic_json:
                if isinstance(dic_json[key], dict):  # 如果dic_json[key]依旧是字典类型
                    for key_down in dic_json[key]:
                        if key_down == '$Matcher':
                            if   isinstance(dic_json[key][key_down], dict) and '$values' in dic_json[key][key_down].keys()    :
                                dic_json[key] = dic_json[key][key_down]['$values']
                            else:
                                dic_json[key] = dic_json[key][key_down]
                            pact_Matcher(dic_json, dic)
                        else:
                            dic[key] = {}
                            pact_Matcher(dic_json[key], dic[key])
                elif isinstance(dic_json[key], (list, tuple)):
                    new_key = []
                    for key_down in dic_json[key]:
                        new_dic={}
                        pact_Matcher(key_down, new_dic)
                        if new_dic:
                            new_key.append(new_dic)
                        else:
                            new_key.append(key_down)
                    dic[key]=new_key
                else:
                    dic[key] = dic_json[key]
    
    def pact_Enum(dic_json,dic):
        '''
        校验规则:枚举匹配
        :param dic_json:
        :param dic:
        :return:
        '''
        if isinstance(dic_json, dict):
            for key in dic_json:
                if isinstance(dic_json[key], dict):  # 如果dic_json[key]依旧是字典类型
                    for key_down in dic_json[key]:
                        if key_down == '$Enum':
                            if isinstance(dic_json[key][key_down], dict) and '$params' in dic_json[key][key_down].keys():
    
                                if  'iterate_list' in    dic_json[key][key_down]['$params']    and   dic_json[key][key_down]['$params']['iterate_list'] :
                                    dic_json[key] =  dic_json[key][key_down]['$values']
                                else:
                                    print(dic_json ,3333)
                                    dic_json[key] = random.choice(dic_json[key][key_down]['$values'])
                            else:
                                dic_json[key] = random.choice(dic_json[key][key_down])
                            pact_Enum(dic_json, dic)
                        else:
                            dic[key] = {}
                            pact_Enum(dic_json[key], dic[key])
                elif isinstance(dic_json[key], (list, tuple)):
                    new_key = []
                    for key_down in dic_json[key]:
                        new_dic={}
                        pact_Enum(key_down, new_dic)
                        if new_dic:
                            new_key.append(new_dic)
                        else:
                            new_key.append(key_down)
                    dic[key]=new_key
                else:
                    dic[key] = dic_json[key]
    
    def pact_Term(dic_json,dic):
        if isinstance(dic_json, dict):
            for key in dic_json:
                if isinstance(dic_json[key], dict):
                    for key_down in dic_json[key]:
                        if key_down == '$Term':
                            if isinstance(dic_json[key][key_down], dict) and '$values' in dic_json[key][key_down].keys():
                                if   isinstance(dic_json[key][key_down]['$params']['example'] ,int):
                                    dic_json[key] = int(str_xeger.xeger(dic_json[key][key_down]['$values']))
                                elif  isinstance(dic_json[key][key_down]['$params']['example'] ,float):
                                    dic_json[key] = float(str_xeger.xeger(dic_json[key][key_down]['$values']))
                                else:
                                    dic_json[key] = str_xeger.xeger(dic_json[key][key_down]['$values'])
                            else:
                                assert False,'契约中的Term类数据不正确'
                            pact_Term(dic_json, dic)
                        else:
                            dic[key] = {}
                            pact_Term(dic_json[key], dic[key])
                elif isinstance(dic_json[key], (list, tuple)):
                    new_key = []
                    for key_down in dic_json[key]:
                        new_dic={}
                        pact_Term(key_down, new_dic)
                        if new_dic:
                            new_key.append(new_dic)
                        else:
                            new_key.append(key_down)
                    dic[key]=new_key
    
                else:
                    dic[key] = dic_json[key]
    
    def gerate_json_based_on_pact(pactverify_json):
        '''
         传入契约,根据契约生成对应的json
         支持
         基本的匹配类型,包括含有参数的情况
            1、Matcher 类,校验规则:值匹配
            2、Like 类,校验规则:类型匹配
            3、EachLike 类,校验规则:数组类型匹配
            4、Term 类,校验规则:正则匹配
            5、Enum 类,校验规则:枚举匹配
         每一级的处理都是独立的递归处理,如果json深度太深,性能会存在问题
        :param pactverify_json:
        :return:
        '''
        try:
            mPactVerify = PactJsonVerify(pactverify_json, hard_mode=True, separator='$')
            test_json = {'test': pactverify_json}
            pact_json = {}  # 生成的json
            # 处理like类型
            pact_json = pact_like(test_json, pact_json)
            # 基于上面的结果处理EachLike类型
            test_json = pact_json
            pact_json = {}
            pact_EachLike(test_json, pact_json)
            # Term 类处理  正则
            test_json = pact_json
            pact_json = {}
            pact_Term(test_json, pact_json)
            # Enum类型处理
            test_json = pact_json
            pact_json = {}
            pact_Enum(test_json, pact_json)
            # pact_Matcher类型处理
            test_json = pact_json
            pact_json = {}
            pact_Matcher(test_json, pact_json, )
    
            pact_json = pact_json['test']
            mPactVerify.verify(pact_json)
    
            if mPactVerify.verify_result == True:
                return pact_json
            else:
                return {'error': 'mockjson 生成失败 ', 'verify_info': mPactVerify.verify_info}
        except  Exception as e:
            return {'error': 'mockjson 生成失败 ', 'verify_info': str(e)}
    
    
    
    
    
    
    
    
    pactverify_json={
        "$Like":{
            "a":{"$EachLike":11},
            "b":{"$Matcher":22},
            "c":{"$Like":22},
            "d":{'$Term': {'$values': r'^\d{1}$','$params': {'example': 1}}},
            'e':{'$Enum': [11, 22]},
            'f':23,
            'g':'rrr',
            "aa":{"$EachLike":{'k1': 'v1'}},
            "aaa":{"$EachLike":['k1', 'v1']},
            'code': 0,
            'msg': {'$Like': {"id": 1,"name": 'lili'}},
            'ms122g': {'$Like': {"id": 1, "name": 'lili','999':    {"$EachLike":11}        }},
            '9232': {'$Like': {"id": 1, "name": 'lili', '999': {"$EachLike": {'k1': {'$Like': {'$values': {'k1': 'v1'},'$params': {'nullable': True}}  }   }}}},
            '19232': {'$Like': {"id": 1, "name": 'lili', '999': {"$EachLike": ['k11', 'v1']}}},
            '192232': {'$Like': {"id": 1, "name": 'lili', '999': {"$EachLike": {'$EachLike': {"id": 1,"name": 'lili'}}       }}},
            'old_data': {'$Like': {
                'msg': 'success',
                'code': { '$Matcher': {'k':'3'}},
                'aa':{ '$Enum': [11, 22]},
                'shuzi':{
                    '$Term': {
                        '$values': r'^\d{1}$',
                        '$params': {'example': 1}
                 }},
                'data': {
                    '$Like': {
                        'type_id':  {'$EachLike':2222},
                        'name': {'$Like': '9999999'},
                        'order_index': {'$Enum': [111, 1223, 166, 121]},
                        'status': 1,
                        'subtitle': {
                            '$Like':{
                                'te22st':555,
                               '331113': {'$Enum': [11, 223, 66, 21]},
                                '9983':{'$EachLike':{'k1': '11111'}}
                            }
                        },
                        'game_name': {'$EachLike':{'test': {'$Enum': [191, 5223, 626, 21111]}   ,
                                                    'test2':{'$Like': '9999999'},
    
                                                   }},
                        'user_name':{'$EachLike': {
                                    '$values': {'k2231': 'v1'},
                                    '$params': {'minimum': 0}
                            }},
                        'kkkk': {'$Enum': {
                                        '$values': [99, 88],
                                        '$params': {'iterate_list': True}
                                    }
                                }
    
                    }
                }
            }},
            'gg': {'$Enum': {'$values': [11, 22],'$params': {'iterate_list': True}}},
            'dde':{'$EachLike': {'$values': {'k1': 'v1'},'$params': {'minimum': 0}}},
            "a111a": {"$EachLike": {'k1':    {'$Enum': {'$values': [11, 22],'$params': {'iterate_list': True} }} ,'232s':{'$Matcher': 12},'3s':{"$Like":22}    }},
            "768833": {"$EachLike": {'k1':    {'$Enum': {'$values': [11, 22],'$params': {'iterate_list': True} }} ,'2228s':{'$Matcher': 12},'3333s':{"$Like":22}    }},
             '8322'  :     {'$Like': {'$values': {'k1': 'v1'},'$params': {'nullable': True}}},
            'age': {
                        '$Term': {
                            '$values': r'^\d{1}$',
                            '$params': {'example': 1, 'key_missable': True}
                        }
                    },
            'm1sg': {
                        '$Matcher': {
                            '$values': 'success',
                            '$params': {'key_missable': True}
                        }
                    },
            'cod9999e': {
                        '$Like': {
                            '$values': 0,
                            '$params': {'key_missable': True}
                        }
                },
        }
    }
    
    
    
    aaa={     "$Like":{   "a111a": {"$EachLike": {'k1':    {'$Enum': {'$values': [11, 22],'$params': {'iterate_list': True} }} ,'232s':{'$Matcher': 12},'3s':{"$Like":22}    }}}}
    import time
    for x in range(400):
        a=time.time()
        print(gerate_json_based_on_pact(pactverify_json))
        print(time.time()-a)
    
  • 现在麻烦的是 EachLike 那里,其他部分都很容易就写出了,今天再搞一天

  • 先递归 LIke 然后是 EachLike 然后到 Matcher-》Term-》Enum 顺序不能乱,不然就有问题, 然后我用生成的 json 和原来的契约做契约校验,看看能不能通过, 只能这样慢慢测试再优化代码了。没想出什么好办法。 领导要我做契约测试的平台, 给消费者提供 mock 功能, 给生产者校验数据, 参考了 pact-Python 和你写的 PactVerify,目前就差一点点了。 我的 okr 全靠这个了

  • 目前刚刚写了 json 契约的反写,5 个匹配类型,每个匹配类型都需要递归处理, 现在是多层嵌套的时候,如果是 [[[]]] 这样的还是存在问题,也有一些新 bug 还在测试中可能没暴露出来,因为还有参数问题的处理,想写出没 bug 的我感觉还得到周 5,不过已经很接近了, 原本以为 pact 包有类似的功能就不用重复造轮子了,

  • 没想到,那么快就有回复, 根据契约来生成一个符合规则的 json ,然后 mock 或者就用来测接口报错什么的,都很方便, 然后就一直在查有没有 已有的方法, 我目前用递归写了一个 但还是有 bug 而且时间比较长,居然要 0.8 秒,昨天发完帖子就开写, 今天花了一整天了

  • 是的, 根据契约来进 mock 数据,结构可控,数据也可控,还有一定的随机性,也能用来生成测试数据,辅助测试,

  • 线上不好交流吧

  • 接口自动化全量字段校验 at 2021年10月28日

    我想知道 ,怎么根据契约生成 mock 数据呢

  • data=str(data)
    data=data.encode('utf-8')
    data = gzip.compress(data)