python實戰-基於正交實驗(工具:allpairs)自動生成介面異常測試用例

wan了個蛋發表於2023-01-30

目前支援的功能

1.基於不同的引數型別(str、int、float、bool、list、tuple、dict、set、None、date、datetime、time、timestamp)自動生成正交測試用例
2.可以自定義引數長度與內容,方便覆蓋邊界值測試
3.提供自定義過濾引陣列合的入口,方便自定義

實現思路

1.抓取api資訊(目前公司用的swagger): https://www.cnblogs.com/qtclm/p/17049176.html ,uri、method、params、response,解析完成後寫入excle
2.讀取抓取完畢的api資訊,處理為allpairs所需要的ordereddict
3.呼叫allpairs工具生成測試用例
4.解析allpairs生成的測試用例(輸出為字串),並處理為dict
5.處理完畢後寫入excel

後期最佳化

1.根據介面響應實現自動斷言
2.增加其他介面平臺的api抓取(openapi、eolink等)
3.增加其他自動化生成用例的方法(有效、無效等價類,流量回放等)
4.整合到平臺

遇到的問題

1.allpairs只支援兩個以上的引數生成,因為引數只有一個時,需要自行處理
2.引數為json巢狀時,allpairs輸出的引數需要額外特殊處理,這塊也是最麻煩的地方
3.可變型別在迴圈過程中儘量使用深複製物件,避免迴圈執行中被意外修改
4.類變數、例項變數需要合理運用,例如迴圈時需要合理的進行初始化

# _*_ coding: UTF-8 _*_
"""
@project -> file : city-test -> rr
@Author          : qinmin.vendor
@Date            : 2023/1/29 19:44
@Desc            : 自動生成介面測試用例:支援正交實驗,等價類,邊界值
"""

import copy
import json
import os
import random
import re
import sys
from collections import OrderedDict
from allpairspy import AllPairs
from utils.operation_datas import operationExcle
import math
from utils.time_utils import timeUtil
from utils.wrapper_util import exec_time_wrapper
from inspect import isfunction

from faker import Faker
'''faker使用教程:https://blog.csdn.net/qq_42412061/article/details/122997802'''

class autoGenrateApiCaseAllpairspy(object):

    time_util = timeUtil()
    fake = Faker(locale="zh_cn")  # 指定語言為中文

    CONST_MAX_STRING_LENGTH = 100
    CONST_MIN_STRING_LENGTH = 1
    # 生成字串相關的資料
    # special_chars是否包含特殊字元;digits是否包含數字;upper_case是否包含大寫字母,lower_case是否包含小寫字母
    special_character = fake.password(length=8, special_chars=True, digits=False, upper_case=False,
                                      lower_case=False)  # 生成隨機特殊字元
    random_str = fake.paragraph()  # 生成隨機字元
    random_str_max = random_str * math.ceil(CONST_MAX_STRING_LENGTH / len(random_str))
    phone_number = fake.phone_number()  # 生成隨機電話號碼

    # 生成時間相關的資料
    random_date = fake.date(pattern="%Y-%m-%d", end_datetime=None)  # 生成隨機日期
    random_start_datetime = time_util.adjust_time(num=-30)
    random_end_datetime = time_util.adjust_time(num=0)
    random_start_timestamp_millisecond = time_util.adjust_time(num=-30, is_timestamp=True, millisecond=True)
    random_end_timestamp_millisecond = time_util.adjust_time(num=0, is_timestamp=True, millisecond=True)
    random_start_timestamp = time_util.adjust_time(num=-30, is_timestamp=True, millisecond=False)
    random_end_timestamp = time_util.adjust_time(num=0, is_timestamp=True, millisecond=False)

    # 生成int與float相關的資料
    random_int = random.randint(1, 100)
    random_float = round(random.random(), 6) + random_int  # 生成隨機字元
    random_int_max = str(random_int) * math.ceil(CONST_MAX_STRING_LENGTH / len(str(random_int)))
    random_float_max = str(str(random_float) *
        math.ceil(CONST_MAX_STRING_LENGTH / len(str(random_float).replace('.', '')))).\
       replace('.','') + "." + str(random_int)


    STRING_ENUM = ["", None, random_str, special_character,
                   random_str_max[:CONST_MAX_STRING_LENGTH], random_str[:CONST_MIN_STRING_LENGTH],
                   random_start_datetime, random_end_datetime, random_date]

    INT_FLOAT_ENUM = [0, None, special_character, random_int, random_float,
                      int(random_int_max[:CONST_MAX_STRING_LENGTH]),
                      int(str(random_int)[:CONST_MIN_STRING_LENGTH]), int(random_float_max[:CONST_MAX_STRING_LENGTH]),
                      int(str(random_float)[:CONST_MIN_STRING_LENGTH]), random_start_timestamp,
                      random_start_timestamp_millisecond, random_end_timestamp, random_end_timestamp_millisecond]
    LIST_ENUM=[[], None, [{}]]
    BOOL_ENUM=[None, False, True,"true","false"]
    OBJECT_ENUM=[None,{}]
    NULL_ENUM=[None,"null"]

    # 定義介面欄位生成的型別與列舉
    CONST_TYPE_ENUM = {
        str: STRING_ENUM,"string": STRING_ENUM,
        int: INT_FLOAT_ENUM,"integer": INT_FLOAT_ENUM,"number": INT_FLOAT_ENUM,
        None: NULL_ENUM, "NONE": NULL_ENUM,"null": NULL_ENUM,
        float: INT_FLOAT_ENUM,"double": INT_FLOAT_ENUM,
        bool: BOOL_ENUM,'bool': BOOL_ENUM,"boolean": BOOL_ENUM,
        dict: OBJECT_ENUM,'object': OBJECT_ENUM,set: LIST_ENUM,list: LIST_ENUM,'array': LIST_ENUM,
    }

    CONST_TYPE_ENUM_COPY = copy.deepcopy(CONST_TYPE_ENUM)

    is_array = False #判斷引數傳入的引數是不是list

    params_key_list = [] #判斷引數的key對應的value是不是list型別,如果是,就將key存進此物件
    extra_cases = [] #存放額外的case,請求引數中的key巢狀json的情況

    excle_path = './'
    excle_name_params_field_detail = 'auto_genrate_api_case_allpairspy_params_field_detail.xlsx'
    excle_name_params_obj = 'auto_genrate_api_case_allpairspy_params_obj_5.xlsx'
    # excle_name_cases_data = 'maint-api.xlsx'
    excle_name_cases_data = 'maint-apiv2-api-docs.xlsx'

    # 遞迴解析json,原文:https://blog.csdn.net/qq_45662588/article/details/122265447
    read_excle_case_field_desc={'case':0,'uri':1,'method':2,"params_path":3,"params_body":5,"response_body":7,"skip":9}
    # 資料處理前的case表頭,寫入excle中最終會刪除params_path,且params_body替換為params && response_body替換為expected
    write_excle_case_field_desc={'case':0,'uri':1,'method':2,'params_path':3,'params_body':4,'response_body':5,'skip':6}

    @classmethod
    def custom_valid_combination(cls,row):
        """
        自定義過濾條件,返回false的條件不會出現在組閤中
        """
        n = len(row)
        if n > 1:
            if row[0] in (None,'',""):
                return False
        return True

    # 指定:自定義過濾函式
    all_pairs_filter_func=custom_valid_combination
    if not (isfunction(all_pairs_filter_func) or isinstance(all_pairs_filter_func,(classmethod,staticmethod))) :
        raise Exception(f"all_pairs_filter_func物件必須是一個方法:{all_pairs_filter_func}")

    @classmethod
    def read_all_cases_data(cls):
        cases_data_obj=operationExcle(excle_path=cls.excle_path,excle_name=cls.excle_name_cases_data)
        lines=cases_data_obj.get_lines()+1
        case_list=[]
        for i in range(lines):
            # yield cases_data_obj.get_row_data(i+1)
            values=cases_data_obj.get_row_data(i+1)
            out_values=[]
            for i in cls.read_excle_case_field_desc:
                out_values.append(values[cls.read_excle_case_field_desc[i]])
            if out_values[0] is not None:
                case_list.append(out_values)
        return case_list

    @classmethod
    def dict_parse_generator(cls,params,pre=None):
        '''遞迴解析json,如果key為對應的值是list且list中的元素不是dict,則不返回'''
        pre = pre[:] if pre else []  # 紀錄value對應的key資訊
        # print("params:",params)
        if isinstance(params,list) :
            params=params[0]
        if isinstance(params, dict):
            for key, value in params.items():
                if isinstance(value, dict):
                    if len(value) > 0:
                        for d in cls.dict_parse_generator(value, pre + [key]):
                            yield d
                    else:
                        yield pre + [key, '{}']
                elif isinstance(value, (list, tuple)):
                    if len(value) > 0:
                        for index, v in enumerate(value):
                            # 紀錄list的下標與key,方便動態提取
                            # print("index",index)
                            for d in cls.dict_parse_generator(v, pre + [key, str(index)]):
                                yield d
                    else:
                        yield pre + [key, '[]'] if isinstance(value,list) else pre + [key,'()']

                else:
                    yield pre + [key, value]

    @classmethod
    def api_prams_convert(cls, params_info):
        '''根據引數與引數型別轉換為預期定義的資料型別定義的列舉'''

        def get_params_field_type(params_field_value):
            if str(params_field_value).startswith('"') and str(params_field_value).endswith('"'):
                _type = 'string' or 'str'
            elif str(params_field_value).startswith('{') and str(params_field_value).endswith("}"):
                _type = 'object' or 'dict'
            elif str(params_field_value).startswith('[') and str(params_field_value).endswith(']'):
                _type = 'array' or 'list'
            elif str(params_field_value) in ('0', '0.0'):
                _type = 'number' or 'int'
            elif str(params_field_value) in ("true", 'false', 'True', 'False'):
                _type = 'boolean' or 'bool'
            elif str(params_field_value) in ('None', 'null', 'nil'):
                _type = 'null' or 'None'
            else:
                _type = 'string' or 'str'
            return _type

        def replace_params_values(params_info):
            '''替換引數所有key的內容'''
            parse_params_info = list(cls.dict_parse_generator(params_info))
            parse_params_info_keys=[i[0] for i in parse_params_info]
            # 將沒有輸出的key新增到字典
            for params_info_key in params_info:
                if params_info_key not in parse_params_info_keys:
                    parse_params_info.append([params_info_key,params_info[params_info_key]])
            for i in range(len(parse_params_info)):
                if parse_params_info[i] :
                    params_key = parse_params_info[i][:-1]
                        # 組裝json提取表示式
                    params_extract_str = "']['".join(params_key)
                    params_extract_str = "['" + params_extract_str + "']"
                    params_extract_str_re = re.search("\['\d+'\]", params_extract_str)
                    if params_extract_str_re:
                        # 去除list索引下標的字串,處理為eval能夠識別的int字串
                        params_extract_str_re = params_extract_str_re.group()
                        params_extract_str = params_extract_str.replace(params_extract_str_re,
                                            params_extract_str_re.replace("'",'').replace('"',""))
                    # 獲取key內容
                    params_field_value=eval(f"params_info{params_extract_str}")
                    params_type=get_params_field_type(params_field_value=params_field_value)
                    # # 將不為空得引數值且不在列舉定義範圍內的資料新增到列舉定義中,避免影響原有資料且用於完善引數覆蓋情況
                    if params_field_value and (params_field_value not in cls.CONST_TYPE_ENUM[params_type]):
                        cls.CONST_TYPE_ENUM[params_type].append(params_field_value)

                    #動態執行程式碼:替換key的內容
                    exec(f"params_info{params_extract_str}={cls.CONST_TYPE_ENUM[params_type]}")

        def append_params_key_is_object_to_params_key_list(params_info):
            '''追加引數key對應的值是object型別的key到指定list中'''
            if isinstance(params_info,list):
                params_info=params_info[0]

            for i in params_info:
                if isinstance(params_info[i],list) and isinstance(params_info[i][0],dict)\
                        and (i not in cls.params_key_list):
                    cls.params_key_list.append(i)

        if not params_info:
            return params_info
        if isinstance(params_info, (list, dict)):
            if isinstance(params_info, list):
                if params_info:
                    params_info = params_info[0]
                cls.is_array = True
        replace_params_values(params_info)
        append_params_key_is_object_to_params_key_list(params_info)
        return params_info

    @classmethod
    def parse_uri_params_path_query(cls,uri: str, params_path: dict):
        '''解析url中的params_path與params_query引數'''
        if not params_path:
            return {},{}
        uri_split = uri.split('/')
        uri_params = [i for i in uri_split if i.startswith('{') and i.endswith('}')]
        params_path_keys = list(params_path.keys())
        uri_params_path_dict = {}  # 儲存:uri中的引數是key的情況
        uri_params_query_dict = {}  # 儲存:uri中的引數不是key的情況,代表是query引數
        for params_path_key in params_path_keys:
            if "{" + params_path_key + "}" in uri_params:
                uri_params_path_dict[params_path_key]=params_path[params_path_key]
            else:
                uri_params_query_dict[params_path_key]=params_path[params_path_key]
        return uri_params_path_dict,uri_params_query_dict

    @classmethod
    def join_request_uri(cls,uri,path_dict,query_dict):
        def out_path_replace_uri(uri,dict_obj):
            '''uri拼接替換後的path引數'''
            if dict_obj:
                for i in dict_obj:
                    uri = uri.replace("{" + i + "}",str(dict_obj[i]) )
                return uri
            return uri
        def out_query_replace_uri(uri,dict_obj):
            '''uri拼接替換後的query引數'''
            if dict_obj:
                uri += "?"
                for i in dict_obj:
                    uri += f"{i}={str(dict_obj[i])}&"
                uri = uri[:-1] if uri[-1] == "&" else uri
            return uri

        if not (path_dict or query_dict):
            return uri
        uri=out_path_replace_uri(uri=uri,dict_obj=path_dict)
        uri=out_query_replace_uri(uri=uri,dict_obj=query_dict)
        uri = cls.time_util.pyobject_to_json_str(uri)
        return uri

    @classmethod
    def out_target_case(cls,src_obj_index, params_all_obj):
        '''根據index返回符合條件的case'''
        if not params_all_obj:
            return {}
        _l = len(params_all_obj) - 1
        if src_obj_index > _l:
            target_case = params_all_obj[random.randint(0, _l)]
        else:
            target_case = params_all_obj[src_obj_index]
        return target_case

    @classmethod
    @exec_time_wrapper(round_num=10,module_obj=__file__,class_obj=sys._getframe().f_code.co_name,is_send_email=False)
    def generate_all_cases(cls, ordered_dict_obj,params_info):

        def convert_allpairs_cases_to_cases(params_keys,ordered_dict_obj,cases_obj):
            '''將allparis輸出的case字串轉換為dict輸出'''
            if len(params_keys)>=2:
                cls.time_util.append_params_key_isobject_to_extra_cases(case_params_key_obj=ordered_dict_obj,
                    params_keys=params_keys,extra_cases=cls.extra_cases,filter_func=cls.all_pairs_filter_func,
                    cases_obj=cases_obj,is_append_extra_cases=False,params_key="")
            else:
                '''params只有一個key時,直接組裝字典輸出(allpairs這種情況處理會輸出空)'''
                cases = ordered_dict_obj
                for case in cases:
                    for case_key in cases[case]:
                        cases_obj.append({case: case_key})

        def append_extra_case_to_case(cases_obj,extra_cases,params_key_list):
            # 將extra_case資料追加到引數中
            for index,out_case in enumerate(cases_obj):
                for params_key in params_key_list:
                    update_cases=[i for i in extra_cases for o in i if o == params_key]
                    extra_case=cls.out_target_case(src_obj_index=index,params_all_obj=update_cases)
                    out_case.update(extra_case)

        def return_out_cases(params_info,ordered_dict_obj):
            '''輸出最終轉換完成的測試用例集合'''
            if isinstance(params_info,list):
                params_info=params_info[0]
            params_keys=[i for i in params_info]
            out_cases = []
            '''將allparis輸出的case字串轉換為dict輸出'''
            convert_allpairs_cases_to_cases(params_keys=params_keys,ordered_dict_obj=ordered_dict_obj,cases_obj=out_cases)
            '''將extra_case資料追加到引數中'''
            append_extra_case_to_case(cases_obj=out_cases,extra_cases=cls.extra_cases,params_key_list=cls.params_key_list)
            return out_cases

        if not (params_info or ordered_dict_obj):
            return []
        return return_out_cases(params_info=params_info,ordered_dict_obj=ordered_dict_obj)

    @classmethod
    def rm_old_excel(cls, excle_path='./', excle_name=''):
        if os.path.exists(os.path.join(excle_path, excle_name)):
            os.remove(os.path.join(excle_path, excle_name))

    @classmethod
    def reset_params_key_list_and_extra_cases(cls):
        '''每個api的引數不同,在寫入後需要呼叫此方法重置,避免元素被重複使用'''
        cls.params_key_list=[]
        cls.extra_cases=[]
        # 還原預設的列舉定義,避免資料一直遞增
        cls.CONST_TYPE_ENUM=cls.CONST_TYPE_ENUM_COPY
        cls.is_array=False

    @classmethod
    def write_params_filed_detail_to_excle(cls, params,ex_obj:operationExcle=None):
        '''將引數欄位明細寫入到excle'''
        ex_obj_cp=copy.deepcopy(ex_obj)
        if ex_obj is None:
            cls.rm_old_excel(excle_path=cls.excle_path, excle_name=cls.excle_name_params_field_detail)
            ex_obj = operationExcle(excle_path=cls.excle_path, excle_name=cls.excle_name_params_field_detail)
        if not isinstance(ex_obj,operationExcle):
            raise Exception(f"傳入的物件預期是:{type(operationExcle)},實際為:{type(ex_obj)}")
        # print("params:",params)
        if isinstance(params,dict):
            params=[params]
        ordered_dict_obj = cls.api_prams_convert(params)
        params_all = cls.generate_all_cases(ordered_dict_obj, params)
        for case in params_all:
            for case_key in case:
                # 如果key存在與list中,說明引數是list,將object轉換為array
                if case_key in cls.params_key_list:
                    case[case_key] = [case[case_key]]
            ex_obj.write_values([str(i) for i in case.values()])
        # 重置引數狀態
        cls.reset_params_key_list_and_extra_cases()
        if ex_obj_cp is None:
            ex_obj.write_values(list(params_all[0].keys()))
            ex_obj.save_workbook()

    @classmethod
    def write_cases_obj_to_excle(cls, case_info, params_path,params_body,ex_obj:operationExcle=None):
        def write_cases_data_main(case_info,params_all_cases,is_body=False):
            '''
            將生成完畢的測試用例寫入到excel,基礎方法(將測試用例明細寫入到excle)
            Args:
                case_info: 原始的測試用例資訊
                params_all_cases: 使用正交生成後的所有測試用例集合
                is_body: 用於區分請求引數是否是body,如果不是bodu,則設定params_bodu為{},因為path與query型別不需要body請求,引數直接拼接到url上了
            Returns:
            '''
            for index, case in enumerate(params_all_cases):
                # print("params_all_cases:",id(params_all_cases))
                path_dict = cls.out_target_case(src_obj_index=index,
                                                params_all_obj=params_all_path) if params_all_path else {}
                query_dict = cls.out_target_case(src_obj_index=index,
                                                 params_all_obj=params_all_query) if params_all_query else {}
                # 拼接uri
                uri = cls.join_request_uri(uri=uri_copy, path_dict=path_dict, query_dict=query_dict)
                # print("path_dict:",path_dict)
                # print("query_dict:",query_dict)
                # # 忽略存在引數為空字元的uri
                # if '//' in uri:
                #     continue
                case_identifying_str = '-介面異常測試用例(基於正交實驗自動生成)'
                case_name = case_info[cls.write_excle_case_field_desc['case']]
                # 拼接測試用例名稱
                if case_identifying_str in case_name:
                    case_name = case_name[:case_name.find(case_identifying_str)]
                case_name = case_name + case_identifying_str + '-' + str(index + 1)
                case_info[cls.write_excle_case_field_desc['case']] = case_name
                case_info[cls.write_excle_case_field_desc['uri']] = uri

                for case_key in case:
                    # 如果key存在與list中,說明引數是list,將object轉換為array
                    if case_key in cls.params_key_list:
                        case[case_key] = [case[case_key]]
                # print("case:",case)
                # 判斷最外層的引數是不是list,是list,將object轉換為array
                if cls.is_array:
                    case_info[cls.write_excle_case_field_desc['params_body']] = json.dumps([case], ensure_ascii=False)
                else:
                    case_info[cls.write_excle_case_field_desc['params_body']] = json.dumps(case, ensure_ascii=False)
                if not is_body:
                    # 從params_body中刪除params_path&params_query對應的key
                    case_info[cls.write_excle_case_field_desc['params_body']] = json.dumps({})
                ex_obj.write_values(case_info)

        # ex_obj_cp=copy.deepcopy(ex_obj)
        if ex_obj is None:
            cls.rm_old_excel(excle_path=cls.excle_path, excle_name=cls.excle_name_params_obj)
            ex_obj = operationExcle(excle_path=cls.excle_path, excle_name=cls.excle_name_params_obj)
        if not isinstance(ex_obj,operationExcle):
            raise Exception(f"傳入的物件預期是:{type(operationExcle)},實際為:{type(ex_obj)}")
        uri=case_info[cls.write_excle_case_field_desc['uri']]
        uri_copy=copy.deepcopy(uri)
        # 根據params_path物件區分uri_path引數、uri_query引數
        uri_params_path_dict,uri_params_query_dict=cls.parse_uri_params_path_query(uri=uri_copy,params_path=params_path)
        # 輸出path與body的ordered_dict物件
        ordered_dict_obj_path = cls.api_prams_convert(uri_params_path_dict)
        ordered_dict_obj_query = cls.api_prams_convert(uri_params_query_dict)
        # 輸出path與body所有的正交測試用例
        params_all_path = cls.generate_all_cases(ordered_dict_obj_path, uri_params_path_dict)
        params_all_query = cls.generate_all_cases(ordered_dict_obj_query, uri_params_query_dict)
        ordered_dict_obj_body = cls.api_prams_convert(params_body)
        params_all_body = cls.generate_all_cases(ordered_dict_obj_body, params_body)

        if params_all_body:
            write_cases_data_main(case_info=case_info,params_all_cases=params_all_body,is_body=True)
        elif params_all_path:
            write_cases_data_main(case_info=case_info, params_all_cases=params_all_path, is_body=False)
        elif params_all_query:
            write_cases_data_main(case_info=case_info, params_all_cases=params_all_query, is_body=False)

        # 重置引數狀態
        cls.reset_params_key_list_and_extra_cases()

    @classmethod
    @exec_time_wrapper(round_num=10,module_obj=__file__,class_obj=sys._getframe().f_code.co_name,is_send_email=True)
    def batch_write_params_filed_detail_to_excle(cls,case_list=None):
        if case_list is None:
            case_list=cls.read_all_cases_data()
        cls.rm_old_excel(excle_path=cls.excle_path, excle_name=cls.excle_name_params_field_detail)
        ex_obj = operationExcle(excle_path=cls.excle_path, excle_name=cls.excle_name_params_field_detail)
        if isinstance(case_list,dict):
            case_list=[case_list]

        # 寫入首行資料
        for params in case_list[1:]:
            # params是一個可變型別,程式執行中共用了此物件,這裡需要傳一個深複製物件給他,避免執行過程中params被替換
            params=copy.deepcopy(params)
            params_body=params[cls.write_excle_case_field_desc['params_body']]
            sheet_name=params[cls.write_excle_case_field_desc['case']][:31]
            sheet_name=cls.time_util.check_filename(sheet_name,priority_matching_chars=[':','/','\\','?','*','[',']'])
            # print("sheet_name:",sheet_name)
            ex_obj.create_sheet(sheet_name)
            ex_obj.data=ex_obj.get_data_for_sheet_name(sheet_name)
            if not isinstance(params_body, dict):
                params_body = json.loads(params_body)
            if isinstance(params_body,list):
                params_body=params_body[0]
            ex_obj.write_values(list(params_body.keys()))
            cls.write_params_filed_detail_to_excle(params=params_body,ex_obj=ex_obj)
        # 刪除預設建立的sheet
        ex_obj.delete_sheet(sheet_name='Sheet')
        ex_obj.save_workbook()

    @classmethod
    @exec_time_wrapper(round_num=10,module_obj=__file__,class_obj=sys._getframe().f_code.co_name,is_send_email=False)
    def batch_write_cases_obj_to_excle(cls,case_info=None,case_list=None):
        if case_list is None:
            case_list=cls.read_all_cases_data()
        if isinstance(case_list, dict):
            case_list = [case_list]
        case_info_first_line=case_info
        if case_info is None:
            case_info_first_line=list(cls.write_excle_case_field_desc.keys())
            # 刪除params_path, 且params_body替換為params & & response_body替換為expected
            case_info_first_line[cls.write_excle_case_field_desc['params_body']]='params'
            case_info_first_line[cls.write_excle_case_field_desc['response_body']]='expected'
        # 刪除舊的檔案
        cls.rm_old_excel(excle_path=cls.excle_path, excle_name=cls.excle_name_params_obj)
        ex_obj = operationExcle(excle_path=cls.excle_path, excle_name=cls.excle_name_params_obj)
        ex_obj.write_values(case_info_first_line)
        for case in case_list[1:]:
            # params是一個可變型別,程式執行中共用了此物件,這裡需要傳一個深複製物件給他,避免執行過程中params被替換
            case_info=copy.deepcopy(case)
            # if case_info[1]==:
            if case_info[1] in (
                    # '/v1/work-order/{tenantId}/{aggregateId}/repair-info',
            # # # #                 # '/v1/wx/user/logout',
            # # # #                     '/v1/wx/work-order/{tenantId}/{userId}/my-work-orders',
            # #     '/v1/work-order/tenant/{tenantId}',
                                '/v1/stakes/section/{sectionId}/number/{stakeSerialNumber}',
            # #                     '/v1/maintenance/search/select-explicit',
            # #                     '/v1/collector/find-aggregate',
                            ):
                params_body=case_info[cls.write_excle_case_field_desc['params_body']]
                params_path=case_info[cls.write_excle_case_field_desc['params_path']]
                if not isinstance(params_body,dict):
                    params_body=json.loads(params_body)
                if not isinstance(params_path,dict):
                    params_path=json.loads(params_path)
                if params_body in ({},[],[{}],None) and params_path in ({},[],[{}],None) :
                    case_info[cls.write_excle_case_field_desc['params_body']]=json.dumps(params_body)
                    case_info[cls.write_excle_case_field_desc['params_path']] = json.dumps(params_path)
                    ex_obj.write_values(case_info)
                else:
                    cls.write_cases_obj_to_excle(case_info=case_info,params_path=params_path,params_body=params_body,ex_obj=ex_obj)

        # 刪除params_path對應的列
        ex_obj.del_ws_cols(cls.write_excle_case_field_desc['params_path']+1,is_save=False)
        #重新命名sheet_name
        ex_obj.rename_sheet_name(src_sheet_name='Sheet',target_sheet_name='all_api_info')
        ex_obj.save_workbook()


if __name__ == "__main__":
    params = {"account": "demo", "pwd": "crmeb.com", "key": "533721295cb06314f4bcaacebc28e3bd", "code": "nbcw",
              "wxCode": "", 'userinfo': {}, 'age': 0, 'is_vip': False, 'ext': None,
              'orders': [{"order": "asc", "fileds": []}], 'orders2': [{"order2": "asc", "fileds2": []}],
              "price2":0.05,"ip":""}


    test_prams=OrderedDict([
        ('k1',[1,2,3]),
        # ('k2',[('order',[1,2,3]),('order2',[4,5,6])])
    ])




    params_array = [params]
    params_array.extend(params_array)

    #
    auto_case = autoGenrateApiCaseAllpairspy()
    path_dict = {'sectionId': 3448477344847734484773448477344847734484773448477344847734484773448477344847734484773448477344847734, 'stakeSerialNumber': '留言一點重要.檔案發展就是資料論壇製作.留言一點重要.檔案發展就是資料論壇製作.留言一點重要.檔案發展就是資料論壇製作.留言一點重要.檔案發展就是資料論壇製作.留言一點重要.檔案發展就是資料論壇製作.'}
    query_dict = {"tenantId2": 22,"df":"432fsfds"}
    # url=auto_case.join_request_uri(uri="/v1/stakes/section/{sectionId}/number/{stakeSerialNumber}",path_dict=path_dict,query_dict=query_dict)
    # print(url)

    # json_str={'breakageThreshold': {}, 'breakageType': ['linear', 'alligator', 'pothole', 'raveling', 'explicit', 'subsidence', 'rut', 'seal', 'patch', 'horizontalCrack', 'verticalCrack'], 'endTime': 0, 'startTime': 0, 'tenantId': 0}
    # print(list(auto_case.dict_parse_generator(json_str)))

    # # 讀取case資料
    # print(auto_case.read_all_cases_data())
    # auto_case.batch_write_cases_obj_to_excle()
    # auto_case.join_uri_params_path('/v1/stakes/section/{sectionId}/number/{stakeSerialNumber}',{"sectionId": 0, "stakeSerialNumber": "","test":123})
    # 單個寫入
    # auto_case.write_params_filed_detail_to_excle(params)
    # auto_case.write_cases_obj_to_excle(case_info, params=params_array)
    # 批次寫入
    # auto_case.batch_write_params_filed_detail_to_excle()
    # print("case_info:before",case_info)
    auto_case.batch_write_cases_obj_to_excle()
    # print("case_info:after",case_info)




相關文章