python3+excel+unittest+ddt+BeautifulReport實現(xiàn)接口自動化測試

? ? ? ? ? ? 本問主要介紹用excel表格來管理接口用例,采用python+unittest測試框架,結合ddt數(shù)據(jù)驅動,最后結合BeautifulReport報告插件,生成最終的測試報告

首先,來3張圖,了解輸入數(shù)據(jù),輸出結果

?????1、需要測試的接口case:execel表格管理

? ??2、請求的body:request_data.py文件中字典req_data,用來存放所有case請求的body

? ? 3、利用unittest+ddt+BeautifulReport生成HTML測試報告:

其次,附上整個項目的結構圖

最后,分解項目運行的細節(jié)內容

1、項目的主運行文件:run_ddt.py

(1)、導入我們run_ddt.py文件運行所需要的第三方包

# coding=utf-8

import unittest

import time

import os

from BeautifulReport import BeautifulReport

(2)、生成我們需要的report路徑

curpath = os.path.dirname(os.path.realpath(__file__))

reprot_path = os.path.join(curpath, "report")

(3)、匹配該項目里,以test開頭的文件,并添加成一個unittest測試集

def add_case(casepath=curpath, rule="test_*.py"):

? ? discover = unittest.defaultTestLoader.discover(casepath, pattern=rule)

? ? return discover

(4)、得到了測試集,便可以運行整個測試集里面的測試用例

def run_case(all_case, reportpath=reprot_path):

? ? now = time.strftime("%Y%m%d%H%M%S")

? ? print('測試報告生成地址: %s' % reportpath)

? ? BeautifulReport(all_case).report(description='用例執(zhí)行情況', filename='測試報告_' + str(now), report_dir=reportpath)

# 該文件的main函數(shù)入口:

if __name__ == '__main__':

? ? cases = add_case()

? ? run_case(cases)

? ? ? ? 寫到上面第3步的時候,你就會聯(lián)想到,我們后續(xù)肯定會編寫一個test_開頭的py文件,而里面就是我們的測試內容。

? ? ? ? 是的,我們第2個文件,就是編寫我們的測試代碼,也可以說是我們的測試思路或者是測試的步驟。

2、測試思路:test_case_all.py

(1)、導入該文件所運行的包,以及從公用模塊導入公用函數(shù)

from myrequests import MyRequests

from common.operationExcel import OperationExcel

from common.operationReqData import OperationReqDate

from common.dependentData import DependentData

from ddt import ddt, data, unpack

from common.operationSQL import connectSQL

from config import *

import json

import unittest

(2)、我們是從一個excel的sheet表拿的數(shù)據(jù),要想把這些數(shù)據(jù)利用ddt來驅動,就需要把整個excel表的數(shù)據(jù)全部拿出來,然后再利用ddt來分割數(shù)據(jù),在用切割的數(shù)據(jù)進行單個case測試

class GetReqData(object):

# 初始化excel操作模塊類,才能調用該類下的函數(shù)方法

? ? def __init__(self):

? ? ? ? self.operation_excel = OperationExcel()

#? 利用excel類里面的方法,獲取excel表格的所有數(shù)據(jù)

? ? def get_data(self):

? ? ? ? exe_data_all = []? ? # 定義一個空的列表,存放excel表格的數(shù)據(jù)

? ? ? ? rows_count = self.operation_excel.get_all_lines()? ? # 獲取表格中有多少行數(shù)據(jù)

? ? ? ? for i in range(1, rows_count):? ? # 循環(huán)遍歷取excel表格的數(shù)據(jù),去除第一行

? ? ? ? ? ? exe_data = self.operation_excel.get_a_row_data(i)? ? # 把每一行的數(shù)據(jù)都取出

? ? ? ? ? ? exe_data_all.append(tuple(exe_data))? ? # 取出的數(shù)據(jù),都添加到定義空列表中

? ? ? ? return exe_data_all? ? # 取值完成后,把所有的數(shù)據(jù)返回出去

# 單獨的把獲取數(shù)據(jù)函數(shù)進行調用一次,這樣ddt數(shù)據(jù)驅動,才有數(shù)據(jù)作為參數(shù)傳入

get_req_data = GetReqData()

req_data = get_req_data.get_data()

(3)、ddt來驅動excel表的數(shù)據(jù),獲取到的excel數(shù)據(jù)是一個list類型,提取每一行的數(shù)據(jù)就顯示輕松多了。提取完數(shù)據(jù),就可以進行request請求測試了。

# 采用ddt數(shù)據(jù)驅動,在運行的類前,就需要先運行ddt的裝飾器函數(shù),故需要在Run類前加上@ddt

@ddt

class Run(unittest.TestCase):

? ? #? 集成unittest.TestCase方法,然而需要初始化,在unittest里__init__函數(shù)無法使用,所以我們就用到unittest里的setUp、?tearDown這樣函數(shù)來做類函數(shù)的初始化,這里初始化只需要運行一次,這里我采用了setUpClass這個函數(shù)來實現(xiàn)

? ? @classmethod

? ? def setUpClass(cls):

? ? ? ? print('------執(zhí)行開始------')

? ? ? ? cls.operation_excel = OperationExcel()

? ? ? ? cls.operation_req_data = OperationReqDate()

? ? ? ? cls.dependent_data = DependentData()

? ? ? ? cls.m = MyRequests()

? ? ? ? cls.host = HOST? ? # 從config文件獲取host,這樣切換地址不用改excel表的url內容

? ? ? ? cls.new_data_dict = {}

? ? @classmethod

? ? def tearDownClass(cls):

? ? ? ? print('------執(zhí)行完畢------')


#? ? ?初始化工作已完成,那就進入我們重點、重點、重點了

? ? @data(*req_data)? ? # ddt下data可以把數(shù)據(jù)進行切分返回數(shù)據(jù),具體可參照ddt使用

? ? @unpack? ? # ddt下的一個方法,目的是把每一行數(shù)據(jù)分開傳參,具體使用ddt詳解

? ? def test_case(cls,? *exe_data):

? ? ? ??# 這里是判斷需要執(zhí)行SQL語句

? ? ? ? if exe_data[3] == "SQL":? ? ? ?

? ? ? ? ? ? sql = exe_data[5]

? ? ? ? ? ? connectSQL(exe_data[7], sql, cls.new_data_dict)

'''

這里判斷case是都需要執(zhí)行(運行的流程重點就在此)

我們從每一行數(shù)據(jù)取出來是一個list,根據(jù)list的下標,獲取excel表格的值;

? ? 1、取決于該case是否運行,如果運行,就往下取值,反之,則不用管;

? ? 2、獲取該case請求的body值,根據(jù)excel的req_data字段,取對應的值

? ? 3、如果該case有依賴,就需要走依賴函數(shù),進行鍵位值的替換,實現(xiàn)實時數(shù)據(jù)變動;

? ? 4、進行接口的請求(如果沒有依賴,則可以跳過第3步)

? ? 5、進行預期結果與實際結果的對比

? ? 6、最后,如果該case需要提取某個字段的值,根據(jù)鍵位,在返回的內容中進行提取

? ? 注意:new_data_dict這個字典,是存放替換的值,格式是key=value,key是我們自定義的名稱,value則是從返回值提取的值,提取數(shù)據(jù)必須在替換數(shù)據(jù)之前就有值,不然會報錯,因為提取的數(shù)據(jù)沒有值,替換的時候就無法找到值進行替換。提取值是用了jsonpath的方法提取,替換則是采用了自己定義的,以"."的方式代替層級關系。

'''

????elif exe_data[3] == 'YES':? ? ? ?# 第1步,判斷是否運行

? ? ? ? ? ? req_data = cls.operation_req_data.get_req_data(exe_data[5])? ? # 第2步取body

? ? ? ? ? ? print('執(zhí)行的用例ID: ', exe_data[0])

? ? ? ? ? ? data = json.loads(exe_data[9])? ? # 數(shù)據(jù)轉換,怕數(shù)據(jù)格式錯誤。

? ? ? ? ? ? if exe_data[7] != '':? ? # 判斷請求的body是否有依賴,此處判斷值為有依賴

? ? ? ? ? ? ? ? req_data = cls.dependent_data.replace_req_data(exe_data[7], req_data, cls.new_data_dict)? ? # 第3步,有依賴,從提取值獲取進行替換(注:提取值必須有值)

? ? ? ? ? ? ? ? res = cls.m.myrequests(cls.host + exe_data[2], req_data, exe_data[4], exe_data[6])? ????? # 第4步,進行數(shù)據(jù)請求

? ? ? ? ? ? ? ? for key, value in data.items():? ? ? ? # 第5步,預期結果與實際結果的對比

? ? ? ? ? ? ? ? ? ? res_value = cls.dependent_data.replace_data(key, res)

? ? ? ? ? ? ? ? ? ? cls.assertEqual(value, res_value)

? ? ? ? ? ? ? ? if exe_data[8] != '':? ? ? ? # 判斷是否需要提取

? ? ? ? ? ? ? ? ? ? cls.new_data_dict = cls.dependent_data.dependent_data(exe_data[8], res, cls.new_data_dict)? ? ? ? # 第6步,根據(jù)鍵位,在返回的內容提取值

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? res = cls.m.myrequests(cls.host + exe_data[2], req_data, exe_data[4], exe_data[6])? ? ????# 第4步,進行無body替換的接口請求

? ? ? ? ? ? ? ? for key, value in data.items():? ? # 第5步,預期結果與實際結果對比

? ? ? ? ? ? ? ? ? ? res_value = cls.dependent_data.replace_data(key, res)

? ? ? ? ? ? ? ? ? ? cls.assertEqual(value, res_value)

? ? ? ? ? ? ? ? if exe_data[8] != '':? ? #? 判斷是否需要提取

? ? ? ? ? ? ? ? ? ? cls.new_data_dict = cls.dependent_data.dependent_data(exe_data[8], res, cls.new_data_dict)? ? # 第6步,根據(jù)提取的鍵位,在返回值中提取對應的值

# 該文件的程序入口

if __name__ == '__main__':

?????unittest.main()


3、整個項目的脊柱已經(jīng)弄好,現(xiàn)在就需要各個內容來支配整個項目

?----從test文件整理出,我們可以察覺到缺少的函數(shù)文件,我們一一列出:

---1。excel表格的數(shù)據(jù)獲取方法

---2。請求body的數(shù)據(jù)獲取方法

---3。提取值的方法

---4。替換body的方法

---5。接口請求的方法

從這5點中,我們就來一一編寫需要的方法:

3-1、excel的獲取數(shù)據(jù)方法:operationExcel.py

????在test文件里,我們發(fā)現(xiàn)了這兩句代碼,屬于excel的操作

rows_count = self.operation_excel.get_all_lines()

exe_data = self.operation_excel.get_a_row_data(i)

????那么我們就需要在common公用文件下新建一個operationExcel.py文件,來針對excel表格數(shù)據(jù)的操作

# coding=utf-8

import xlrd, os, time, xlwt

from xlutils.copy import copy

class OperationExcel(object):

? ? def __init__(self, file_name=None, sheet_id=None):

? ? ? ? if file_name:

? ? ? ? ? ? self.file_name = file_name

? ? ? ? ? ? self.sheet_id = sheet_id

? ? ? ? else:

? ? ? ? ? ? self.file_name = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'data/ApiList1.xlsx')

? ? ? ? self.data = self.get_data()


? ? # 獲取數(shù)據(jù)

? ? def get_data(self):

? ? ? ? data = xlrd.open_workbook(self.file_name)

? ? ? ? tables = data.sheets()[self.sheet_id]

? ? ? ? return tables


? ? # 獲取sheet下的行數(shù)

? ? def get_all_lines(self):

? ? ? ? tables = self.data

? ? ? ? return tables.nrows


? ? # 獲取某一行的內容

? ? def get_a_row_data(self, row_num):

? ? ? ? tables = self.data

? ? ? ? row_data = tables.row_values(row_num)

? ? ? ? return row_data

3-2、body的獲取數(shù)據(jù)方法:operationReqData.py

? ? 在test文件里,我們會發(fā)現(xiàn)以下的代碼:

req_data = cls.operation_req_data.get_req_data(exe_data[5])

? ? 這樣的代碼,是我們從excel表取標識字段,到request_data文件里req_data取對應key的value,這樣body就能取出來了

from data import request_data

class OperationReqDate(object):

? ? def __init__(self):

? ? ? ? self.data = request_data.req_data? # 修改req_data的文件名

? ? # 根據(jù)關鍵key來獲取req_data文件的內容

? ? def get_req_data(self, key):

? ? ? ? if key == '':

? ? ? ? ? ? return None

? ? ? ? return self.data.get(key)

3-3、根據(jù)excel的數(shù)據(jù),提取返回值的內容:dependentData.py

? ? ????我們在現(xiàn)實的測試中,往往發(fā)現(xiàn),這個接口運行的時候,會調用上一個接口的數(shù)據(jù),而且還有一些數(shù)據(jù)值,都是重復調用,總不可能請求一個接口,去多次調用其他接口吧,這樣就導致了接口的請求量變大了,增加了服務器的負載能力。

? ? ? ? 解決方案:我們在請求前,我們新建一個空的字典,自定義key來獲取對應的value值,成成一個新的字典,請求body需要的時候,就直接從這里取值,這樣就減少了請求次數(shù)。

? ? ? ? 然而,在test文件中,我們會發(fā)現(xiàn)有這樣的代碼存在:

if exe_data[8] != '':

? ? ? ? ? ? ? ? ? ? cls.new_data_dict = cls.dependent_data.dependent_data(exe_data[8], res, cls.new_data_dict)

? ? 這樣是進行判斷是否有提取值,有則需要提取,反之則不需要,然后我們的提取方法:

? ? 提取方法的思路:

? ? ? ? 1、根據(jù)依賴的鍵位,去遍歷返回的res,鍵位提取的格式:id=(result.id)

? ? ? ? 2、找到了鍵位的值后,把鍵位的key作為字典的key存放,找的值當做value存放,組成一個新的字典

# 根據(jù)exe表格中的key=(value)來獲取一個新增的dict-data

? ? def dependent_data(self, dependent_data, res, data_dict):

? ? ? ? exe_data = dependent_data.split('\n')

? ? ? ? # print('11: ', exe_data)

? ? ? ? for data in exe_data:

? ? ? ? ? ? data_value = data.split('=')

? ? ? ? ? ? # print('data_value:', data_value)

? ? ? ? ? ? dependent_key = data_value[1]

? ? ? ? ? ? value = self.replace_data(dependent_key, res)

? ? ? ? ? ? data_dict[data_value[0]] = value

? ? ? ? return data_dict

在此時,就會發(fā)現(xiàn)一個新的語句:

value = self.replace_data(dependent_key, res)

然而我們就需要在該文件下再創(chuàng)建一個函數(shù)方法,這里提取的方法是采用jsonpath:

def replace_data(self, data_key, data_value):

? ? ? ? """

? ? ? ? :param data_key: 依賴的key值

? ? ? ? :param data_value: 遍歷的返回頁面數(shù)據(jù)

? ? ? ? :return:

? ? ? ? """

? ? ? ? try:

? ? ? ? ? ? json_exe = parse(data_key)

? ? ? ? ? ? madles = json_exe.find(data_value)

? ? ? ? except Exception as msg:

? ? ? ? ? ? print(msg)

? ? ? ? return [madle.value for madle in madles][0]

????????這樣我們的程序就不會報錯,該方法的用途我也不做多解釋,網(wǎng)上有類似的專業(yè)講解。那么我們繼續(xù)我們項目其他方法解析

3-4、替換請求的body里的鍵位值:dependentData.py

? ? 在3-3中,我已經(jīng)講解了提取值的方法,主要是為了便于替換的時候需要,在新的一個字典里,我們只有傳入key,就能把之前接口請求返回的value取到,進行替換,就可以直接請求了,我在excel表的替換值的格式:id={{user_id}},格式可以根據(jù)自己喜歡來寫,切割點就需要重新變化下即可。

# 根據(jù)exe的表格key={{value}}去替換值

? ? def replace_req_data(self, dependent_data, req_data, new_data_dict):

? ? ? ? exe_data = dependent_data.split('\n')

? ? ? ? for data in exe_data:

? ? ? ? ? ? data_value = data.split('=')

? ? ? ? ? ? value = new_data_dict.get(data_value[1][2:-2])

? ? ? ? ? ? req_data = self.check_json_data.check_json_data(req_data, data_value[0], value)

? ? ? ? return req_data

? ? 在上面的方法中,發(fā)現(xiàn)有一行新的代碼

?req_data = self.check_json_data.check_json_data(req_data, data_value[0], value)

? ? 這行代碼是進行替換的操作,遍歷操作替換的工作量大,因此我們重新編寫一個文件來實現(xiàn)此功能:

3-4-1、數(shù)據(jù)替換方法:checkJsonData.py

? ? 遍歷我們的請求的body,根據(jù)對應的鍵位,去實現(xiàn)value的一個更新,實現(xiàn)數(shù)據(jù)更新功能

# coding=utf-8

from httprunner import exceptions, logger

from httprunner.compat import OrderedDict, basestring, is_py2

class Check_Json_Data(object):

? ? # 替換json數(shù)據(jù)中對應的value

? ? def change_json(self, json_content, query, new, delimiter='.'):

? ? ? ? raise_flag = False

? ? ? ? response_body = u"response body: {}\n".format(json_content)

? ? ? ? try:

? ? ? ? ? ? keys = query.split(delimiter)

? ? ? ? ? ? if len(keys) == 1:

? ? ? ? ? ? ? ? if isinstance(json_content, (list, basestring)):

? ? ? ? ? ? ? ? ? ? json_content[int(keys[0])] = new

? ? ? ? ? ? ? ? elif isinstance(json_content, dict):

? ? ? ? ? ? ? ? ? ? json_content[keys[0]] = new

? ? ? ? ? ? if len(keys) > 1:

? ? ? ? ? ? ? ? for key in keys:

? ? ? ? ? ? ? ? ? ? if isinstance(json_content, (list, basestring)):

? ? ? ? ? ? ? ? ? ? ? ? return self.change_json(json_content[int(key)], ".".join(keys[1:]), new, delimiter='.')

? ? ? ? ? ? ? ? ? ? elif isinstance(json_content, dict):

? ? ? ? ? ? ? ? ? ? ? ? return self.change_json(json_content[key], ".".join(keys[1:]), new, delimiter='.')

? ? ? ? except (KeyError, ValueError, IndexError):

? ? ? ? ? ? raise_flag = True

? ? ? ? if raise_flag:

? ? ? ? ? ? err_msg = u"Failed to extract! => {}\n".format(query)

? ? ? ? ? ? err_msg += response_body

? ? ? ? ? ? logger.log_error(err_msg)

? ? ? ? ? ? raise exceptions.ExtractFailure(err_msg)


# 數(shù)據(jù)替換的方法

? ? def check_json_data(self, old_req_data, dependent_key, values):

? ? ? ? """

? ? ? ? 把舊的請求數(shù)據(jù),根據(jù)鍵位,替換掉舊數(shù)據(jù)

? ? ? ? :param old_req_data: json文件的舊數(shù)據(jù)

? ? ? ? :param dependent_key: excel表中的鍵位值

? ? ? ? :param values: 獲取依賴的接口返回的鍵位值,也就是新值

? ? ? ? :return: 返回一個替換后的請求數(shù)據(jù)

? ? ? ? """

? ? ? ? self.change_json(old_req_data, dependent_key, values)

? ? ? ? return old_req_data

3-5、請求的方法:myrequests.py

? ? 我們采用request模塊進行url的請求,這里需要更新自己的token,各個平臺不同,token的取值也不同,這個因系統(tǒng)而異。

? ? 首先,我們需要提取token

????# 獲取token

? ? def login(self):

? ? ? ? global token

? ? ? ? if "Authorization" in self.s.headers.keys():? ? ? ? # 判斷是否存在token,如果有就直接跳過

? ? ? ? ? ? # print('--------token is exits!!---------')

? ? ? ? ? ? return self.s

? ? ? ? else:

? ? ? ? ? ? excel_data = self.operation_excel.get_a_row_data(1)

? ? ? ? ? ? url = self.host + excel_data[2]

? ? ? ? ? ? req_data = login_data

? ? ? ? ? ? res = self.s.post(url, json=req_data)

? ? ? ? ? ? r = res.content.decode('utf-8')

? ? ? ? ? ? r = json.loads(r)

? ? ? ? ? ? token = r['result']['token']

? ? ? ? ? ? self.s.headers.update({"Authorization": token})

? ? ? ? ? ? print("----------token create successfully!--------")

? ? ? ? ? ? return self.s

????????其次,封裝自己的請求方式。網(wǎng)上有很多種封裝方式,小伙伴可以選擇自己喜歡的封裝方式,這里我貼上我自己的封裝方式,方法不完美,能實現(xiàn)就好。

????# 自定義請求函數(shù)

? ? def myrequests(self, url, req_data, req_type, data_type):

? ? ? ? """

? ? ? ? 自定義請求函數(shù)

? ? ? ? :param url: 請求的url

? ? ? ? :param req_data: 請求的data

? ? ? ? :param req_type: 請求方式

? ? ? ? :param data_type: 數(shù)據(jù)的傳遞格式

? ? ? ? :return: res頁面結果

? ? ? ? """

? ? ? ? if req_type == "POST":? # 判斷請求方式:POST

? ? ? ? ? ? if data_type == 'JSON':? # 判斷請求參數(shù)的數(shù)據(jù)類型

? ? ? ? ? ? ? ? # post_data = json.loads(req_data)

? ? ? ? ? ? ? ? # res = self.login().post(url, json=req_data)

? ? ? ? ? ? ? ? res = self.login().post(url, json=req_data)

? ? ? ? ? ? elif data_type == '':

? ? ? ? ? ? ? ? res = self.login().post(url, data=req_data)

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? res = self.login().post(url, data=req_data)

? ? ? ? elif req_type == "GET":

? ? ? ? ? ? if data_type == 'JSON':

? ? ? ? ? ? ? ? get_params = json.loads(req_data)

? ? ? ? ? ? ? ? res = self.login().get(url, params=get_params)

? ? ? ? ? ? elif data_type == '':

? ? ? ? ? ? ? ? res = self.login().get(url, params=req_data)

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? res = self.login().get(url, params=req_data)

? ? ? ? elif req_type == "DELETE":

? ? ? ? ? ? if data_type == 'JSON':

? ? ? ? ? ? ? ? get_params = json.loads(req_data)

? ? ? ? ? ? ? ? res = self.login().delete(url, params=get_params)

? ? ? ? ? ? elif data_type == '':

? ? ? ? ? ? ? ? res = self.login().delete(url, params=req_data)

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? res = self.login().delete(url, params=req_data)

? ? ? ? print("request_req_url: ", res.url)

? ? ? ? print("request_req_data: ", req_data)

? ? ? ? res = res.content

? ? ? ? res = json.loads(res.decode('utf-8'))

? ? ? ? print('res: ', res)

? ? ? ? return res


在附上配置文件內容:config

# coding=utf-8

HOST = "http://172.16.62.66"

# HOST = "http://172.16.62.71"

SQL_IP = "172.16.62.66"

# SQL_IP = "172.16.62.71"

db_message = {

? ? ? ? "host": SQL_IP,

? ? ? ? "username": "root",

? ? ? ? "password": "123456",

? ? ? ? "port": 3306,

? ? ? ? "charset": "utf8"

}

login_url = HOST + '/xxx-x'x'x'x/login/login'

login_data = {

? ? ? ? "mobile": "18100000000",

? ? ? ? "smsCode": "888888"

}


????????總結:項目的方法封裝不是很好,這里介紹我使用的辦法,如果有更好的方法,方便留言,多多研究,讓自動化測試更加完美。郵件的發(fā)送方法,請求頭文件的更新,我這邊都沒做,后期實現(xiàn)了,再更新。。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 再次強調,因為視頻中實戰(zhàn)地址已無法訪問,建議大家根據(jù)原理,用自己公司的業(yè)務邏輯、代碼來練手。本人公司使用pyhon...
    DayBreakL閱讀 887評論 0 2
  • 本文是《手把手教你用Python實現(xiàn)接口自動化測試》這一系列文章的開篇,筆者將從本文開始給大家介紹一下如何使用Py...
    Lxn的小二閱讀 1,671評論 0 15
  • 我一生都在追求 怎么入睡 這是一件極大的事情 于我 夏日的蚊子實在是太厲害了 總能想到辦法咬我一口,吸走我的血 得...
    石頭Q晴閱讀 312評論 0 0
  • 冊頁尺寸:45×32cm 生宣冊頁 潘承,江蘇鹽城人,自幼習畫,師承徐忠平,近僧等先生。
    潘承青衿墨堂閱讀 1,532評論 0 2
  • (一)分詠 一行側影南飛去,萬里橫云上繞開。 (二)七唱 雁聲遠送清秋冷,山色閑流碧水寒。 青山送去天涯月,鴻雁傳...
    嬋月舞羅衣閱讀 1,589評論 1 15

友情鏈接更多精彩內容