實(shí)用性Mock的API接口---django框架(python)

一税朴、背景介紹

  • 因在版本迭代過(guò)程中,一般都需要調(diào)用接口來(lái)實(shí)現(xiàn)需求業(yè)務(wù)瘩燥。而前后端或各系統(tǒng)之間都存在強(qiáng)依賴(lài)性秕重,故構(gòu)思了此Mock接口;主要解決如下場(chǎng)景痛點(diǎn):
  • 1厉膀、前后端的依賴(lài)關(guān)系:如前端已開(kāi)發(fā)完畢但后端還沒(méi)完成溶耘,導(dǎo)致前端無(wú)法進(jìn)行調(diào)試;
  • 2、外部系統(tǒng)依賴(lài)關(guān)系:如外部系統(tǒng)未開(kāi)發(fā)完或者環(huán)境的因素?zé)o法完成對(duì)接調(diào)試;
  • 3服鹅、測(cè)試階段依賴(lài)關(guān)系:測(cè)試的某些場(chǎng)景無(wú)法模擬下凳兵,可調(diào)用mock接口設(shè)置自定義返回值,從而達(dá)到測(cè)試場(chǎng)景的覆蓋(主要就是這塊企软,因咱就是干測(cè)試滴)庐扫;

二、構(gòu)思設(shè)計(jì)及主要功能點(diǎn)

2.1)仗哨、主要功能介紹:

1形庭、 接口可自定義規(guī)則:對(duì)mock接口依據(jù)配置規(guī)則,做字段數(shù)據(jù)必填項(xiàng)校驗(yàn)厌漂、數(shù)據(jù)類(lèi)型校驗(yàn)
2萨醒、 接口可自定義匹配:可設(shè)置數(shù)據(jù)匹配規(guī)則,且支持設(shè)置多個(gè)匹配值苇倡;
3富纸、 接口支持多級(jí)url、動(dòng)態(tài)url旨椒、多種請(qǐng)求方式(get/post/put等)
4胜嗓、 接口狀態(tài)碼自定義:可自定義設(shè)置接口返回狀態(tài)碼
5、 響應(yīng)數(shù)據(jù)-可自定義變量返回:可依據(jù)python代碼設(shè)置自定義變量(如時(shí)間戳钩乍、UUID等變量)
6辞州、 接口可自定義控制關(guān)閉和開(kāi)啟,關(guān)閉后將無(wú)法調(diào)用
7寥粹、 響應(yīng)數(shù)據(jù)后門(mén)屬性:可依據(jù)請(qǐng)求指定key后变过,返回請(qǐng)求中的value數(shù)據(jù)

2.2)埃元、功能/思路導(dǎo)圖:

功能點(diǎn)及思路導(dǎo)圖

三、直接先看最后的成果:

  • 1媚狰、Mock/路由配置頁(yè)面:定義接口路徑以及相應(yīng)的規(guī)則.


    1.路由表截圖--添加

    2.路由表截圖--列表
  • 2岛杀、Mock/接口響應(yīng)數(shù)據(jù)頁(yè)面:定義接口匹配數(shù)據(jù)及返回的數(shù)據(jù)


    1.Mock響應(yīng)數(shù)據(jù)截圖--添加

    2.Mocke響應(yīng)數(shù)據(jù)截圖--列表
  • 3、實(shí)際調(diào)用結(jié)果:調(diào)用接口驗(yàn)證有效性.


    調(diào)用驗(yàn)證1:python調(diào)用

    調(diào)用驗(yàn)證2:接口測(cè)試頁(yè)面使用

四崭孤、上代碼說(shuō)明( 因瑣碎點(diǎn)太多类嗤,只抽取主流點(diǎn)和關(guān)鍵點(diǎn)作為說(shuō)明)

  • 作為一個(gè)測(cè)試小白,比不了他們專(zhuān)業(yè)的開(kāi)發(fā)人員辨宠。這小項(xiàng)目整整一個(gè)月斷斷續(xù)續(xù)的寫(xiě)完了遗锣,其中還是有很多優(yōu)化空間,后續(xù)再慢慢調(diào)整嗤形;
  • 各位大佬精偿,如有優(yōu)化或其他構(gòu)思思路歡迎指點(diǎn)評(píng)論,我再修改修改赋兵;


    代碼目錄結(jié)構(gòu)
1笔咽、從開(kāi)始的路由方面:
  • 1.1)、主路由:此處為入口的主路由霹期,依據(jù)settings配置中ROOT_URLCONF字段判斷主路由位置
#settings配置中ROOT_URLCONF字段判斷主路由位置
ROOT_URLCONF = 'project.urls'
urlpatterns = [
    path(r'admin/mockapi/index/', views.IndexClass.index, name='index-test'),  # 接口測(cè)試頁(yè)面
    path('admin/', admin.site.urls),#django后臺(tái)管理頁(yè)面路由
    path('api/mock/', include('mockapi.urls')),#通用接口的路由
    path('api/test/', include('testapi.urls')),#自定義接口的路由
]
urlpatterns += staticfiles_urlpatterns()
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
  • 1.2)叶组、二級(jí)路由(以api/mock為例):找到二級(jí)路由后并進(jìn)入對(duì)應(yīng)的views(視圖)中,既進(jìn)入視圖邏輯:
urlpatterns = [
    path(r'index', IndexClass.index, name='index-test'),  # 接口測(cè)試頁(yè)面
    path('caseid/registno', views.Caseid.as_view(), name='caseid'),
]
add_path = [
    re_path(r'(\w+)/(\w+)/(\w+)', views.CurrencyRoute.as_view(), name='test3-api-viewstwo'),  # 動(dòng)態(tài)路由+3
    re_path(r'(\w+)/(\w+)', views.CurrencyRoute.as_view(), name='test2-api-viewstwo'),  # 動(dòng)態(tài)路由+2
    re_path(r'(\w+)', views.CurrencyRoute.as_view(), name='test1-api-viewstwo'),  # 動(dòng)態(tài)路由+1
]
urlpatterns.extend(add_path)
2历造、從views視圖方面:
  • \color{blue}{2.1)甩十、通用接口功能下的邏輯處理}
class CurrencyRoute(views.APIView, Currency):
    '''通用模式下-API接口視圖類(lèi)'''
    def __init__(self):
        super(Currency, self).__init__()
        self.code = ['code', '-1']
        self.msg = ['msg']
        self.data = ['data', None]
        self.DataCheck = DataCheck(self.code, self.msg, self.data)
        self.response_data = {self.code[0]: self.code[1], self.msg[0]: None, self.data[0]: self.data[1]}
    # 根據(jù)url找路由表數(shù)據(jù):路由id、條件匹配規(guī)則帕膜、字段要求規(guī)則
    def __getroute(self, args, UrlType, replace='/api/mock/', APIOnOff=True):
        # route = "/".join(list(args)) if re_path.find('-') == -1 else self.find_path("/", re_path, args)
        route = self.request.path.replace(replace, '')
        try:
            get_data = RouteTabMock.objects.get(CustomUrl=route, UrlType=UrlType, APIOnOff=APIOnOff)
        except:
            # 兼容動(dòng)態(tài)url接口枣氧,目前只支持兩級(jí)動(dòng)態(tài),樣例:test/case/{variable}/{variable}
            route_url = [m.start() for m in re.finditer('/', route)]
            for count_i in range(1, len(route_url)):
                try:
                    get_data = RouteTabMock.objects.get(CustomUrl=route[:route_url[-count_i]] + r'/{}' * count_i,
                                                        UrlType=UrlType, APIOnOff=APIOnOff)
                    get_data.ConditionValue = [route[route_url[-count_i] + 1:]]
                except RouteTabMock.DoesNotExist:
                    if count_i == len(route_url) - 1:
                        raise '動(dòng)態(tài)url匹配模式下,未匹配該url=' + route
                else:
                    break
        re_data = get_data.id, get_data.ConditionValue, get_data.APIRequired
        logger.info('路由id={},條件匹配={},必填及類(lèi)型規(guī)則={}'.format(re_data[0], re_data[1], re_data[2]))
        return re_data
    # 接口請(qǐng)求處理垮刹,數(shù)據(jù)處理/規(guī)則處理
    def __requestprocess(self, path_a):
        logger.info(
            '{}\n請(qǐng)求類(lèi)型={},地址={},報(bào)文={}'.format(80 * '=', self.request.method, self.request.path, self.request.data))
                # 提取及處理request數(shù)據(jù)
        if self.request.method == 'GET':
            request_data = dict()
            for key, value in dict(self.request.GET).items():
                request_data[key] = value[0]
        else:
            request_data = dict(self.request.data)
        # 預(yù)留一個(gè)后門(mén)屬性达吞,如果有此test_data字段直接返回
        if Currency.test_data(request_data) != None:
            if self.request.method == 'GET':
                re_data = eval(request_data['test_data'])
            else:
                re_data = request_data['test_data']
            return re_data
        # 獲取路由id、條件匹配規(guī)則荒典、必填及數(shù)據(jù)類(lèi)型規(guī)則
        try:
            re_dataid, conditionValue, apirequired = self.__getroute(path_a, UrlType=self.request.method)
        except:
            msg = '路由表未配置該url:' + self.request.path
            self.response_data[self.msg[0]] = msg
            logger.error('{},請(qǐng)求返回值={}'.format(msg, self.response_data))
            return self.response_data
        # 對(duì)傳入?yún)?shù)進(jìn)行必填項(xiàng)校驗(yàn)和數(shù)據(jù)類(lèi)型的校驗(yàn)酪劫,校驗(yàn)不通過(guò)為False,返回Response錯(cuò)誤的響應(yīng)體
        re_data = self.DataCheck.check(eval(apirequired), request_data)
        if re_data != True:
            return re_data
        # 依據(jù)條件匹配規(guī)則寺董,找到入?yún)?duì)應(yīng)的條件查詢(xún)值
        QueryValue = Querymethod().match_value(request_data, conditionValue, self.request.method)
        logger.info('條件匹配規(guī)則結(jié)果:' + str(QueryValue))
        # 如無(wú)條件匹配規(guī)則覆糟,取該接口下最新mock數(shù)據(jù)
        if QueryValue == False:
            try:
                getdb_data = APIResponseMock.objects.filter(CustomUrl_id=re_dataid)
                db_data = list(getdb_data.values())[-1]
                ResponseData, ResponseCode = self.re_variable(db_data['ResponseData']), db_data['ResponseCode']
                re_data = json.loads(ResponseData), ResponseCode
            except IndexError:
                msg = 'mock表未配置該url{}對(duì)應(yīng)的數(shù)據(jù)'.format(self.request.path)
                self.response_data[self.msg[0]] = msg
                logger.error(msg)
        # 如果有條件匹配規(guī)則,對(duì)應(yīng)的匹配值遮咖;如果存在多個(gè)匹配值滩字,取第一個(gè)匹配的mock值
        else:
            try:
                con = self.Q_model(QueryValue, re_dataid)
                getdb_data = APIResponseMock.objects.filter(con)
                db_data = list(getdb_data.values())[-1]
                ResponseData, ResponseCode = self.re_variable(db_data['ResponseData']), db_data['ResponseCode']
                re_data = json.loads(ResponseData), ResponseCode
            except Exception as ex:
                msg = '未找到匹配值:' + str(QueryValue)
                re_data = {self.code[0]: self.code[1], self.msg[0]: msg, self.data[0]: self.data[1]}
                logger.error(msg)
        logger.info('請(qǐng)求返回值=' + str(re_data))
        return re_data
    # __接口狀態(tài)碼處理
    def __request(self, response_data):
        if type(response_data) == type(tuple()):
            if str(response_data[1]) in ('None', ''):
                response_data = response_data[0], 200
            re_data, status_code = response_data
        else:
            re_data, status_code = response_data, 200
        return re_data, status_code
    # post請(qǐng)求方式
    def post(self, request, *args):
        re_data, status_code = self.__request(self.__requestprocess(args))
        return Response(re_data, status=status_code)
    # Get請(qǐng)求方式
    def get(self, request, *args):
        re_data, status_code = self.__request(self.__requestprocess(args))
        return Response(re_data, status=status_code)
    # Put請(qǐng)求方式
    def put(self, request, *args):
        re_data, status_code = self.__request(self.__requestprocess(args))
        return Response(re_data, status=status_code)

*主要邏輯流介紹(不具體分解,詳見(jiàn)上述代碼內(nèi)注釋?zhuān)?br> 1、視圖第一層處理:后門(mén)屬性的判斷麦箍,以及提取和返回處理
2漓藕、視圖第二層處理:截取url后綴,得到url后找到路由表下對(duì)應(yīng)的字段規(guī)則及匹配規(guī)則挟裂;
3享钞、視圖第三層處理:得到接口字段規(guī)則后,對(duì)路由進(jìn)行必傳和數(shù)據(jù)類(lèi)型規(guī)則校驗(yàn)诀蓉;
4栗竖、視圖第四層處理:依據(jù)接口匹配規(guī)則,取入?yún)⒅档絤ock數(shù)據(jù)表查找匹配值并響應(yīng)數(shù)據(jù)渠啤;

  • \color{blue}{2.2)狐肢、視圖第三層處理,必填項(xiàng)/數(shù)據(jù)類(lèi)型校驗(yàn)處理:}
class DataCheck(object):
    '''數(shù)據(jù)必填+類(lèi)型校驗(yàn)'''
    def __init__(self, code=['code', '-1'], msg=['msg'], data=['data', None]):
        self.key_list = []
        self.code = code
        self.msg = msg
        self.data = data
    # 校驗(yàn)必填項(xiàng)以及數(shù)據(jù)類(lèi)型埃篓,如果不匹配拋出異常
    def __value_type(self, dict_b, type_a):
        dict_data = dict_b
        try:
            for keys in self.key_list:
                dict_data = dict_data[keys]
            if type(dict_data) != type(type_a) and type_a != True:
                raise ValueError('[{}]字段類(lèi)型校驗(yàn)失敗处坪,期望傳入類(lèi)型為:{}'.format(keys, type(type_a)))
        except KeyError:
            if type_a == True:
                raise ValueError('必填校驗(yàn)失敗,缺少必要參數(shù):' + str(keys))
    def check(self, check_a, dict_b):
        try:
            # 為空不做校驗(yàn)根资,與頁(yè)面規(guī)則一致
            if check_a == {}:
                return True
            re_data = self.get_dict_allkeys(check_a, dict_b)
        except Exception as ex:
            # 校驗(yàn)檢查不通過(guò),返回的報(bào)文架专;可通過(guò)類(lèi)屬性修改自定義key
            ex = str(ex).strip('()')
            re_data = {self.code[0]: self.code[1], self.msg[0]: ex, self.data[0]: self.data[1]}
            logger.error('入?yún)⒉环闲r?yàn)規(guī)則={},請(qǐng)求返回值={}'.format(check_a, re_data))
        return re_data
    # 無(wú)限遍歷dict所有key,遞歸方式調(diào)用
    def get_dict_allkeys(self, check_a, dict_b):
        # 使用isinstance檢測(cè)數(shù)據(jù)類(lèi)型,dict遞歸
        if isinstance(check_a, dict):
            for x in range(len(check_a)):
                temp_key = list(check_a.keys())[x]
                temp_value = check_a[temp_key]
                if type(temp_value) in (type({}), type([])):
                    self.key_list.append(temp_key)
                self.get_dict_allkeys(temp_value, dict_b)  # 遞歸遍歷
            # 回退dict的上一層結(jié)構(gòu)
            self.key_list = self.key_list[:-1]
        elif isinstance(check_a, list):
            for key_a in check_a:
                if isinstance(key_a, dict):
                    # for x in range(len(key_a)):
                    # 遞歸到列表+元素位置0,即只支持列表第一個(gè)作為校驗(yàn)點(diǎn)
                    temp_key = list(key_a.keys())[0]
                    temp_value = key_a[temp_key]
                    self.key_list.append(0)
                    self.key_list.append(temp_key)
                    self.get_dict_allkeys(temp_value, dict_b)
                # 必填校驗(yàn),兼容true,True
                if str(key_a) in ('true', 'True'):
                    self.__value_type(dict_b, True)
                    continue
                # 數(shù)據(jù)類(lèi)型校驗(yàn)玄帕,目前校驗(yàn)str/int/bool(兼容縮寫(xiě)及全拼)
                if str(key_a) in ('str', 'string', "<class 'str'>"):
                    self.__value_type(dict_b, str())
                elif str(key_a) in ('int', 'integer', "<class 'int'>"):
                    self.__value_type(dict_b, int())
                elif str(key_a) in ('bool', 'boolean', "<class 'bool'>"):
                    self.__value_type(dict_b, bool())
            # 回退dict的上一層結(jié)構(gòu)
            self.key_list = self.key_list[:-1]
        return True

*主要邏輯處理介紹:
1部脚、無(wú)限遍歷,如為dict既遞歸裤纹,如為list且再找到必填和數(shù)據(jù)類(lèi)型的輸入枚舉值既做規(guī)則校驗(yàn)處理
2委刘、如果不符合校驗(yàn)規(guī)則,既響應(yīng)對(duì)應(yīng)的錯(cuò)誤信息.
*備注:說(shuō)實(shí)話(huà)鹰椒,這塊的邏輯太復(fù)雜了锡移,主要場(chǎng)景太多了;自己測(cè)的時(shí)候改bug改到崩潰漆际,思路進(jìn)入了死循環(huán)淆珊。也體會(huì)了下開(kāi)發(fā)改bug的痛苦了;

  • \color{blue}{-2.3)奸汇、視圖第四層處理施符,匹配規(guī)則的處理:}
class Querymethod(object):

    def match_value(self, databody, conditionValue, req_type):
        '''
        依據(jù)接口設(shè)置匹配規(guī)則,獲取請(qǐng)求參數(shù)中的value擂找;以此作為查詢(xún)條件
        :param databody: 接口請(qǐng)求參數(shù)數(shù)據(jù)-type=dict()
        :param conditionValue: 接口配置的匹配規(guī)則條件-type=dict()
        :return: 根據(jù)匹配規(guī)則,返回請(qǐng)求體中value
        '''
        # 遞歸引用列表取key,直至到value為'True', 'true'結(jié)束
        def key_list(key_data, lista=list()):
            for keys, values in key_data.items():
                if type(values) == type({}) or str(values) in ('True', 'true'):
                    lista.append(keys)
                    True if str(values) in ('True', 'true') else key_list(values)
                elif type(values) == type([]):
                    lista.append(keys)
                    lista.append(0)
                    key_list(values[0], lista)
                else:
                    if lista == []:
                        lista.append(keys)
                    break
            return lista
        # 依據(jù)分號(hào)切割規(guī)則數(shù)據(jù)戳吝,再依次根據(jù)規(guī)則匹配databody對(duì)應(yīng)值
        if type(conditionValue) == type([]):
            return conditionValue
        elif str(conditionValue).replace(' ', '') != '{}':
            re_listdata = []
            for key_data in str(conditionValue).split(";"):
                re_data = databody
                try:
                    lista = key_list(eval(key_data))
                    for value in lista:
                        re_data = re_data[value]
                except  KeyError:
                    if len(str(conditionValue).split(";")) == 1:
                        return False
                    lista.clear()
                else:
                    re_listdata.append(re_data)
                    lista.clear()
            re_data = re_listdata
        else:
            logger.warning('未找到到匹配的數(shù)據(jù)值,匹配規(guī)則=' + str(conditionValue))
            re_data = False
        return re_data

*主要邏輯處理介紹:
1、先判斷是否空字典“{}”贯涎,如是返回False,既后續(xù)其他邏輯返回最新的一條數(shù)據(jù)听哭;
2、依據(jù)分號(hào)" ; "切割,依據(jù)接口定義的匹配規(guī)則陆盘,依次找出入?yún)⒅邢鄳?yīng)的值且警;
2、匹配值以列表形式存儲(chǔ),多個(gè)匹配值情況后續(xù)邏輯將依據(jù)正序條件命中并響應(yīng)對(duì)應(yīng)的數(shù)據(jù)礁遣;

3斑芜、從models模型方面:
  • \color{blue}{3.1)、RouteTabMock路由配置表:}
class RouteTabMock(models.Model):
    """Mock-路由URL表模型  """
    re_type = [("POST", "Post"), ("GET", "Get"), ("PUT", "Put")]
    APIOnOff = models.BooleanField(default=True, verbose_name='接口啟用狀態(tài)')
    UrlType = models.CharField(max_length=6, choices=re_type, default="Post", verbose_name='請(qǐng)求方式')
    UrlName = models.CharField(max_length=20, db_index=True, unique=True, verbose_name='接口名稱(chēng)')
    CustomUrl = models.CharField(max_length=50, db_index=True, unique=True, verbose_name='url地址')
    UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新時(shí)間')
    ConditionValue = models.TextField(default='{}', verbose_name='條件匹配規(guī)則')
    APIRequired = models.TextField(default='{}', verbose_name='字段要求規(guī)則')
    Remarks = models.CharField(max_length=200, null=True, blank=True, default='-', verbose_name='備注說(shuō)明')
    class Meta:
        verbose_name = 'Mock/路由配置表'
        verbose_name_plural = 'Mock/路由配置表'
    # json格式校驗(yàn)祟霍,避免輸入錯(cuò)誤格式:字段要求規(guī)則杏头、條件匹配規(guī)則
    def remarksdisplay(self):
        re_data = models_fun().list_setting(self.Remarks, intdata=20)
        return re_data
    remarksdisplay.allow_tage = True
    remarksdisplay.short_description = '備注說(shuō)明'
    def __str__(self):
        return '{}:[{}]'.format(self.UrlName, self.CustomUrl)
DB-路由配置表

*主要邏輯處理介紹:

  • 1、本次數(shù)據(jù)存儲(chǔ)引用django默認(rèn)配置的sqlite3方式,為文件形式存儲(chǔ)沸呐;主要是輕量級(jí)醇王、且便捷,如后期mock數(shù)據(jù)量過(guò)大時(shí)崭添,可替換專(zhuān)業(yè)的數(shù)據(jù)庫(kù)(mysql或oracle這些寓娩,修改setting中DATABASES字段即可)
  • 2、各字段的設(shè)計(jì)呼渣、定義棘伴,class Meta為后端顯示的名稱(chēng)(也可自定義表名,不添加DB的表名:既為子項(xiàng)目名稱(chēng)+類(lèi)型)屁置;
  • 3焊夸、clean既對(duì)頁(yè)面添加路由時(shí),對(duì)字段要求規(guī)則及條件匹配規(guī)則做json格式校驗(yàn)蓝角;
  • 4阱穗、apirequired是解決后臺(tái)列表字段內(nèi)容超長(zhǎng)時(shí),列表高度被拉長(zhǎng)情況(調(diào)用后使鹅,當(dāng)輸入內(nèi)容超出設(shè)置值時(shí)揪阶,超長(zhǎng)部分將顯示"....";詳解如下圖)


    apirequired調(diào)用的models_fun().list_setting方法
  • \color{blue}{3.2)、接口響應(yīng)數(shù)據(jù)表說(shuō)明:}
class APIResponseMock(models.Model):
    """Mock-接口數(shù)據(jù)響應(yīng)表模型 """
    CustomUrl = models.ForeignKey(RouteTabMock, on_delete=models.PROTECT, verbose_name='關(guān)聯(lián)接口')
    UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新時(shí)間')
    QueryValue = models.CharField(max_length=50, db_index=True, verbose_name='查詢(xún)值')
    ResponseData = models.TextField(verbose_name='響應(yīng)信息數(shù)據(jù)')
    ResponseCode = models.PositiveIntegerField(null=True, blank=True, default=200, verbose_name='接口狀態(tài)碼')
    Remarks = models.CharField(max_length=200, null=True, blank=True, default='-', verbose_name='備注說(shuō)明')
    def clean(self):
        try:
            json.loads(self.ResponseData)
        except json.JSONDecodeError:
            #兼容~{variable}變量形式寫(xiě)入患朱,即轉(zhuǎn)為空字典在做json檢查
            try:
                str_data = re.findall(r"~{(.+?)}", self.ResponseData)
                replace_data = self.ResponseData.replace("~{" + str_data[0] + "}", "{}")
                json.loads(replace_data)
            except IndexError:
                raise ValidationError('輸入的json數(shù)據(jù)變量不符合規(guī)則鲁僚,請(qǐng)檢查')
            except json.JSONDecodeError:
                raise ValidationError('輸入的json數(shù)據(jù)不合法,請(qǐng)檢查')
    class Meta:
        unique_together = ('CustomUrl', 'QueryValue',)
        verbose_name = 'Mock/接口響應(yīng)數(shù)據(jù)表'
        verbose_name_plural = 'Mock/接口響應(yīng)數(shù)據(jù)表'
    def reprofile(self):
        re_data = models_fun().list_setting(self.ResponseData)
        return re_data
    reprofile.allow_tage = True
    reprofile.short_description = '響應(yīng)信息數(shù)據(jù)'
mock數(shù)據(jù)表

*主要邏輯處理介紹:

  • 1麦乞、如上一個(gè)節(jié)點(diǎn)的路由配置表同理蕴茴,相同的邏輯;就不再單獨(dú)說(shuō)明了姐直;
4倦淀、admin后臺(tái)管理頁(yè)面設(shè)置方面(以路由表舉例):
class RouteTabMockAdmin(admin.ModelAdmin):
    def time_seconds(self, obj):
        return obj.UpdateTime.strftime("%Y-%m-%d %H:%M:%S")
    # 時(shí)間格式化處理
    time_seconds.admin_order_field = 'UpdateTime'
    time_seconds.short_description = '更新時(shí)間'
    list_display = ['id', 'UrlName', 'CustomUrl', 'UrlType', 'apirequired', 'routeprofile', 'time_seconds',
                    'remarksdisplay',
                    'APIOnOff']
    list_filter = ['CustomUrl']
    search_fields = ['CustomUrl', 'UrlName']
    list_per_page = 15  # 分頁(yè)展示
    actions_on_top = False  # 頁(yè)面刪除觸發(fā)指令
    actions_on_bottom = True  # 頁(yè)面Action觸發(fā)指令
    fieldsets = [
        ("接口信息", {"fields": ['UrlName', 'CustomUrl', 'UrlType']}),
        ("接口規(guī)則", {"fields": ['APIOnOff', 'APIRequired', 'ConditionValue', 'Remarks']})
    ]
admin.site.register(RouteTabMock, RouteTabMockAdmin)

五、日志模塊方面:

class Log(object):
    level_relations = { 'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, 'crit': logging.CRITICAL}  # 日志級(jí)別關(guān)系映射
    def __init__(self, filename, level='info', when='D', backCount=3,
                 fmt='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
        self.logger = logging.getLogger(filename)
        format_str = logging.Formatter(fmt)  # 設(shè)置日志格式
        self.logger.setLevel(self.level_relations.get(level))  # 設(shè)置日志級(jí)別
        sh = logging.StreamHandler()  # 往屏幕上輸出
        sh.setFormatter(format_str)  # 設(shè)置屏幕上顯示的格式
        th = handlers.TimedRotatingFileHandler(filename=filename, when=when, backupCount=backCount,
                                               encoding='utf-8')  # 往文件里寫(xiě)入#指定間隔時(shí)間自動(dòng)生成文件的處理器
        # 實(shí)例化TimedRotatingFileHandler
        # interval是時(shí)間間隔声畏,backupCount是備份文件的個(gè)數(shù)撞叽,如果超過(guò)這個(gè)個(gè)數(shù)姻成,就會(huì)自動(dòng)刪除,when是間隔的時(shí)間單位愿棋,單位有以下幾種:
        # S 秒\M 分\H 小時(shí)\D 天\W 每星期(interval==0時(shí)代表星期一)\midnight 每天凌晨
        th.setFormatter(format_str)  # 設(shè)置文件里寫(xiě)入的格式
        self.logger.addHandler(sh)  # 把對(duì)象加到logger里
        self.logger.addHandler(th)
def create_file():
    filepath = os.path.realpath(__file__)
    output_dir = os.path.abspath(os.path.join(filepath, "../../logs"))
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    log_name = '{}.log'.format(time.strftime('%Y-%m-%d'))
    filename = os.path.join(output_dir, log_name)
    return filename
filename = create_file()
log = Log(filename, level='debug')
logger = log.logger
log日志內(nèi)容圖片

*主要邏輯處理介紹:
1科展、引用的話(huà),直接logger.info('輸入打印內(nèi)容')或者其他等級(jí)的即可糠雨。簡(jiǎn)單實(shí)用才睹,可收藏為敬;
2甘邀、此日志引用的是logging庫(kù)琅攘,百度一下一大推;能力有限松邪,寫(xiě)不出那么強(qiáng)大的坞琴。是直接復(fù)制過(guò)來(lái)改一改逗抑,達(dá)到我想要的效果邮府。

**備注:其他測(cè)試接口頁(yè)面邏輯流羞酗、自定義接口邏輯流、簡(jiǎn)易封裝欺嗤、及瑣碎配置就不再單獨(dú)列舉了;內(nèi)容太多,估計(jì)看都能看煩.

六吆玖、結(jié)束收尾語(yǔ):

  • 1沾乘、這個(gè)小項(xiàng)目翅阵,寫(xiě)寫(xiě)停停倒騰了一個(gè)月也總算勉強(qiáng)的完成了滥崩;目前準(zhǔn)備在實(shí)際測(cè)試項(xiàng)目上應(yīng)用钙皮,將能滿(mǎn)足一些測(cè)試場(chǎng)景的需求以及提高測(cè)試效率;
  • 2慌烧、因還有很多邏輯以及細(xì)節(jié)就不再單獨(dú)拿出來(lái)說(shuō)明了,具體都有對(duì)應(yīng)的注釋?zhuān)勺孕辛私獠榭?
  • 3汹粤、目前構(gòu)思或者代碼中應(yīng)該還有很多優(yōu)化,會(huì)在下個(gè)版本進(jìn)行優(yōu)化芹壕;
  • 4、下次版本新增功能點(diǎn):
    --- 1)睁壁、接口支持表單形式潘明;
    --- 2)、接口請(qǐng)求類(lèi)型支持:PUT牲阁、PATCH备燃、DELETE并齐;
    --- 3)、接口測(cè)試頁(yè)面依據(jù)postman參考重新設(shè)計(jì)頁(yè)面和功能测垛;
    --- 4)、接口不滿(mǎn)足規(guī)則時(shí)锯七,返回的錯(cuò)誤信息支持自定義結(jié)構(gòu)
mock系統(tǒng)使用說(shuō)明pdf鏈接: https://pan.baidu.com/s/1JV8O66WLJ4q8Oh8UdsC-_A?pwd=8888 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巨双,一起剝皮案震驚了整個(gè)濱河市畏妖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖淘邻,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扶平,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人源譬,你說(shuō)我怎么就攤上這事喉祭》豪樱” “怎么了藐唠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵自赔,是天一觀(guān)的道長(zhǎng)润脸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)涩馆,這世上最難降的妖魔是什么稠项? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任拗胜,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爬立。我一直安慰自己壤圃,他們只是感情好乍桂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布旺芽。 她就那樣靜靜地躺著悯舟,像睡著了一般抵怎。 火紅的嫁衣襯著肌膚如雪奋救。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天反惕,我揣著相機(jī)與錄音尝艘,去河邊找鬼。 笑死姿染,一個(gè)胖子當(dāng)著我的面吹牛背亥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盔粹,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼隘梨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了舷嗡?” 一聲冷哼從身側(cè)響起轴猎,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎进萄,沒(méi)想到半個(gè)月后捻脖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡中鼠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年可婶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片援雇。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡矛渴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情具温,我是刑警寧澤蚕涤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站铣猩,受9級(jí)特大地震影響揖铜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜达皿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一天吓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧峦椰,春花似錦龄寞、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至冤竹,卻和暖如春拂封,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹦蠕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工冒签, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钟病。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓萧恕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親肠阱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子票唆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容