網(wǎng)站建設(shè)用什么軟件鄭州官網(wǎng)網(wǎng)站推廣優(yōu)化公司
原來的web頁面功能測試轉(zhuǎn)變成接口測試,之前大多都是手工進(jìn)行,利用postman和jmeter進(jìn)行的接口測試,后來,組內(nèi)有人講原先web自動化的測試框架移駕成接口的自動化框架,使用的是java語言,但對于一個學(xué)java,卻在學(xué)python的我來說,覺得python比起java更簡單些,所以,寫python的接口自動化測試框架
1、首先,我們先來理一下思路。
正常的接口測試流程是什么?
腦海里的反應(yīng)是不是這樣的:
確定測試接口的工具 —>?配置需要的接口參數(shù) —> 進(jìn)行測試 —> 檢查測試結(jié)果(有的需要數(shù)據(jù)庫輔助) —> 生成測試報告(html報告)
那么,我們就根據(jù)這樣的過程來一步步搭建我們的框架。在這個過程中,我們需要做到業(yè)務(wù)和數(shù)據(jù)的分離,這樣才能靈活,達(dá)到我們寫框架的目的。只要好好做,一定可以成功。這也是我當(dāng)初對自己說的。
接下來,我們來進(jìn)行結(jié)構(gòu)的劃分。
我的結(jié)構(gòu)是這樣的,大家可以參考下:
?
?????? ?common:存放一些共通的方法
? result:執(zhí)行過程中生成的文件夾,里面存放每次測試的結(jié)果
? testCase:用于存放具體的測試case
? testFile:存放測試過程中用到的文件,包括上傳的文件,測試用例以及 ? ? 數(shù)據(jù)庫的sql語句
? caselist:txt文件,配置每次執(zhí)行的case名稱
????????????????????????????????? ? ? ?config:配置一些常量,例如數(shù)據(jù)庫的相關(guān)信息,接口的相關(guān)信息等
????????????????????????????????????? ? readConfig: 用于讀取config配置文件中的內(nèi)容
????????????????????????????????????? ? runAll:用于執(zhí)行case
?既然整體結(jié)構(gòu)有了劃分,接下來就該一步步的填充整個框架了,首先,我們先來看看config.ini和readConfig.py兩個文件,從他們?nèi)胧?#xff0c;個人覺得比較容易走下去噠。
我們來看下文件的內(nèi)容是什么樣子的:
[DATABASE]
host = 50.23.190.57
username = xxxxxx
password = ******
port = 3306
database = databasename[HTTP]
# 接口的url
baseurl = http://xx.xxxx.xx
port = 8080
timeout = 1.0[EMAIL]
mail_host = smtp.163.com
mail_user = xxx@163.com
mail_pass = *********
mail_port = 25
sender = xxx@163.com
receiver = xxxx@qq.com/xxxx@qq.com
subject = python
content = "All interface test has been complited\nplease read the report file about the detile of result in the attachment."
testuser = Someone
on_off = 1
相信大家都知道這樣的配置文件,沒錯,所有一成不變的東西,我們都可以放到這里來。哈哈,怎么樣,不錯吧。
現(xiàn)在,我們已經(jīng)做好了固定的“倉庫”。來保存我們平時不動的東西,那么,我們要怎么把它拿出來為我所用呢?這時候,readConfig.py文件出世了,它成功的幫我們解決了這個問題,下面就讓我們來一睹它的廬山真面目吧。
import os
import codecs
import configparserproDir = os.path.split(os.path.realpath(__file__))[0]
configPath = os.path.join(proDir, "config.ini")class ReadConfig:def __init__(self):fd = open(configPath)data = fd.read()# remove BOMif data[:3] == codecs.BOM_UTF8:data = data[3:]file = codecs.open(configPath, "w")file.write(data)file.close()fd.close()self.cf = configparser.ConfigParser()self.cf.read(configPath)def get_email(self, name):value = self.cf.get("EMAIL", name)return valuedef get_http(self, name):value = self.cf.get("HTTP", name)return valuedef get_db(self, name):value = self.cf.get("DATABASE", name)return value
怎么樣,是不是看著很簡單啊,我們定義的方法,根據(jù)名稱取對應(yīng)的值,是不是so easy?!當(dāng)然了,這里我們只用到了get方法,還有其他的例如set方法,有興趣的同學(xué)可以自己去探索下,這里我們就不在累述了。
話不多說,我們先來看下common到底有哪些東西。
?
既然配置文件和讀取配置文件我們都已經(jīng)完成了,也看到了common里的內(nèi)容,接下來就可以寫common里的共通方法了,從哪個下手呢?今天,我們就來翻“Log.py”的牌吧,因?yàn)樗潜容^獨(dú)立的,我們單獨(dú)跟他打交道,也為了以后它能為我們服務(wù)打下良好基礎(chǔ)。
這里呢,我想跟大家多說兩句,對于這個log文件呢,我給它單獨(dú)啟用了一個線程,這樣在整個運(yùn)行過程中,我們在寫log的時候也會比較方便,看名字大家也知道了,這里就是我們對輸出的日志的所有操作了,主要是對輸出格式的規(guī)定,輸出等級的定義以及其他一些輸出的定義等等??傊?#xff0c;你想對log做的任何事情,都可以放到這里來。我們來看下代碼,沒有比這個更直接有效的了。
import logging
from datetime import datetime
import threading
首先,我們要像上面那樣,引入需要的模塊,才能進(jìn)行接下來的操作。
class Log:def __init__(self):global logPath, resultPath, proDirproDir = readConfig.proDirresultPath = os.path.join(proDir, "result")# create result file if it doesn't existif not os.path.exists(resultPath):os.mkdir(resultPath)# defined test result file name by localtimelogPath = os.path.join(resultPath, str(datetime.now().strftime("%Y%m%d%H%M%S")))# create test result file if it doesn't existif not os.path.exists(logPath):os.mkdir(logPath)# defined loggerself.logger = logging.getLogger()# defined log levelself.logger.setLevel(logging.INFO)# defined handlerhandler = logging.FileHandler(os.path.join(logPath, "output.log"))# defined formatterformatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')# defined formatterhandler.setFormatter(formatter)# add handlerself.logger.addHandler(handler)
,現(xiàn)在,我們創(chuàng)建了上面的Log類,在__init__初始化方法中,我們進(jìn)行了log的相關(guān)初始化操作。具體的操作內(nèi)容,注釋已經(jīng)寫得很清楚了(英文有點(diǎn)兒差,大家看得懂就行,嘿嘿……),這樣,log的基本格式已經(jīng)定義完成了,至于其他的方法,就靠大家自己發(fā)揮了,畢竟每個人的需求也不同,我們就只寫普遍的共用方法啦。接下來,就是把它放進(jìn)一個線程內(nèi)了,請看下面的代碼:
class MyLog:log = Nonemutex = threading.Lock()def __init__(self):pass@staticmethoddef get_log():if MyLog.log is None:MyLog.mutex.acquire()MyLog.log = Log()MyLog.mutex.release()return MyLog.log
看起來是不是沒有想象中的那樣復(fù)雜啊,哈哈哈,就是這樣簡單,python比java簡單了許多,這也是我為什么選擇它的原因,雖然小編我也是剛剛學(xué)習(xí),還有很多不懂的地方。希望大家跟我一同進(jìn)步。好了,至此log的內(nèi)容也結(jié)束了,是不是感覺自己棒棒噠~其實(shí),無論什么時候,都不要感到害怕,要相信“世上無難事只怕有心人”。
下面,我們繼續(xù)搭建,這次要做的,是configHttp.py的內(nèi)容。沒錯,我們開始配置接口文件啦!(終于寫到接口了,是不是很開心啊~)
下面是接口文件中主要部分的內(nèi)容,讓我們一起來看看吧。
import requests
import readConfig as readConfig
from common.Log import MyLog as LoglocalReadConfig = readConfig.ReadConfig()class ConfigHttp:def __init__(self):global host, port, timeouthost = localReadConfig.get_http("baseurl")port = localReadConfig.get_http("port")timeout = localReadConfig.get_http("timeout")self.log = Log.get_log()self.logger = self.log.get_logger()self.headers = {}self.params = {}self.data = {}self.url = Noneself.files = {}def set_url(self, url):self.url = host + urldef set_headers(self, header):self.headers = headerdef set_params(self, param):self.params = paramdef set_data(self, data):self.data = datadef set_files(self, file):self.files = file# defined http get methoddef get(self):try:response = requests.get(self.url, params=self.params, headers=self.headers, timeout=float(timeout))# response.raise_for_status()return responseexcept TimeoutError:self.logger.error("Time out!")return None# defined http post methoddef post(self):try:response = requests.post(self.url, headers=self.headers, data=self.data, files=self.files, timeout=float(timeout))# response.raise_for_status()return responseexcept TimeoutError:self.logger.error("Time out!")return None
這里我們就挑重點(diǎn)來說吧。首先,可以看到,小編這次是用python自帶的requests來進(jìn)行接口測試的,相信有心的朋友已經(jīng)看出來了,python+requests這個模式是很好用的,它已經(jīng)幫我們封裝好了測試接口的方法,用起來很方便。這里呢,我就拿get和post兩個方法來說吧。(平時用的最多的就是這兩個方法了,其他方法,大家可以仿照著自行擴(kuò)展)
- get方法
????? ? 接口測試中見到最多的就是get方法和post方法,其中,get方法用于獲取接口的測試,說白了,就是說,使用get的接口,都不會對后臺數(shù)據(jù)進(jìn)行更改,而且get方法在傳遞參數(shù)后,url的格式是這樣的:http://接口地址?key1=value1&key2=value2,是不是看起來很眼熟啊~(反正我看著它很眼熟~\(≧▽≦)/~啦啦啦),那我們要怎么使用它呢,請繼續(xù)往下看。
對于requests提供的get方法,有幾個常用的參數(shù):
url:顯而易見,就是接口的地址url啦
headers:定制請求頭(headers),例如:content-type = application/x-www-form-urlencoded
params:用于傳遞測試接口所要用的參數(shù),這里我們用python中的字典形式(key:value)進(jìn)行參數(shù)的傳遞。
timeout:設(shè)置接口連接的最大時間(超過該時間會拋出超時錯誤)
現(xiàn)在,各個參數(shù)我們已經(jīng)知道是什么意思了,剩下的就是往里面填值啦,是不是機(jī)械式的應(yīng)用啊,哈哈,小編我就是這樣機(jī)械般的學(xué)習(xí)的啦~
舉個栗子:
url=‘http://api.shein.com/v2/member/logout’ header={‘content-type’: application/x-www-form-urlencoded} param={‘user_id’: 123456,‘email’: 123456@163.com} timeout=0.5 requests.get(url, headers=header, params=param, timeout=timeout)
- post方法
? ? ? ? 與get方法類似,只要設(shè)置好對應(yīng)的參數(shù),就可以了。下面就直接舉個栗子,直接上代碼吧:
url=‘http://api.shein.com/v2/member/login’ header={‘content-type’: application/x-www-form-urlencoded} data={‘email’: 123456@163.com,‘password’: 123456} timeout=0.5 requests.post(url, headers=header, data=data, timeout=timeout)
怎么樣,是不是也很簡單啊。這里我們需要說明一下,post方法中的參數(shù),我們不在使用params進(jìn)行傳遞,而是改用data進(jìn)行傳遞了。哈哈哈,終于說完啦,下面我們來探(了)討(解)下接口的返回值。
依然只說常用的返回值的操作。
text:獲取接口返回值的文本格式
json():獲取接口返回值的json()格式
status_code:返回狀態(tài)碼(成功為:200)
headers:返回完整的請求頭信息(headers['name']:返回指定的headers內(nèi)容)
encoding:返回字符編碼格式
url:返回接口的完整url地址
以上這些,就是常用的方法啦,大家可自行取之。
關(guān)于失敗請求拋出異常,我們可以使用“raise_for_status()”來完成,那么,當(dāng)我們的請求發(fā)生錯誤時,就會拋出異常。在這里提醒下各位朋友,如果你的接口,在地址不正確的時候,會有相應(yīng)的錯誤提示(有時也需要進(jìn)行測試),這時,千萬不能使用這個方法來拋出錯誤,因?yàn)閜ython自己在鏈接接口時就已經(jīng)把錯誤拋出,那么,后面你將無法測試期望的內(nèi)容。而且程序會直接在這里當(dāng)?shù)?#xff0c;以錯誤來計(jì)。(別問我怎么知道的,因?yàn)槲揖褪菧y試的時候發(fā)現(xiàn)的)
好了。接口文件也講完了,是不是感覺離成功不遠(yuǎn)了呢?嗯,如果各位已經(jīng)看到了這里,那么恭喜大家,下面還有很長的路要走~哈哈哈,就是這么任性。(畢竟小編我為了讓各位和我差不多的小白能夠更容易理解,也是使出了體內(nèi)的洪荒之力啦)
慢慢地長嘆一口氣,繼續(xù)下面的內(nèi)容。。。
快,我想學(xué)(看)習(xí)(看)common.py里的內(nèi)容。
import os
from xlrd import open_workbook
from xml.etree import ElementTree as ElementTree
from common.Log import MyLog as LoglocalConfigHttp = configHttp.ConfigHttp()
log = Log.get_log()
logger = log.get_logger()# 從excel文件中讀取測試用例
def get_xls(xls_name, sheet_name):cls = []# get xls file's pathxlsPath = os.path.join(proDir, "testFile", xls_name)# open xls filefile = open_workbook(xlsPath)# get sheet by namesheet = file.sheet_by_name(sheet_name)# get one sheet's rowsnrows = sheet.nrowsfor i in range(nrows):if sheet.row_values(i)[0] != u'case_name':cls.append(sheet.row_values(i))return cls# 從xml文件中讀取sql語句
database = {}
def set_xml():if len(database) == 0:sql_path = os.path.join(proDir, "testFile", "SQL.xml")tree = ElementTree.parse(sql_path)for db in tree.findall("database"):db_name = db.get("name")# print(db_name)table = {}for tb in db.getchildren():table_name = tb.get("name")# print(table_name)sql = {}for data in tb.getchildren():sql_id = data.get("id")# print(sql_id)sql[sql_id] = data.texttable[table_name] = sqldatabase[db_name] = tabledef get_xml_dict(database_name, table_name):set_xml()database_dict = database.get(database_name).get(table_name)return database_dictdef get_sql(database_name, table_name, sql_id):db = get_xml_dict(database_name, table_name)sql = db.get(sql_id)return sql
上面就是我們common的兩大主要內(nèi)容了,什么?還不知道是什么嗎?讓我告訴你吧。
- 我們利用xml.etree.Element來對xml文件進(jìn)行操作,然后通過我們自定義的方法,根據(jù)傳遞不同的參數(shù)取得不(想)同(要)的值。
- 利用xlrd來操作excel文件,注意啦,我們是用excel文件來管理測試用例的。
聽起來會不會有點(diǎn)兒懵,小編剛學(xué)時也很懵,看文件就好理解了。
excel文件:
?
xml文件:
?
至于具體的方法,我就不再一點(diǎn)點(diǎn)講解了,總覺得大家都懂(小編剛學(xué),望諒解),只是我個人需要詳細(xì)記錄,以后容易溫習(xí)。
接下來,我們看看數(shù)據(jù)庫和發(fā)送郵件吧(也可根據(jù)需要,不寫該部分內(nèi)容)
先看老朋友“數(shù)據(jù)庫”吧。
小編這次使用的是MySQL數(shù)據(jù)庫,所以我們就以它為例吧。
import pymysql
import readConfig as readConfig
from common.Log import MyLog as LoglocalReadConfig = readConfig.ReadConfig()class MyDB:global host, username, password, port, database, confighost = localReadConfig.get_db("host")username = localReadConfig.get_db("username")password = localReadConfig.get_db("password")port = localReadConfig.get_db("port")database = localReadConfig.get_db("database")config = {'host': str(host),'user': username,'passwd': password,'port': int(port),'db': database}def __init__(self):self.log = Log.get_log()self.logger = self.log.get_logger()self.db = Noneself.cursor = Nonedef connectDB(self):try:# connect to DBself.db = pymysql.connect(**config)# create cursorself.cursor = self.db.cursor()print("Connect DB successfully!")except ConnectionError as ex:self.logger.error(str(ex))def executeSQL(self, sql, params):self.connectDB()# executing sqlself.cursor.execute(sql, params)# executing by committing to DBself.db.commit()return self.cursordef get_all(self, cursor):value = cursor.fetchall()return valuedef get_one(self, cursor):value = cursor.fetchone()return valuedef closeDB(self):self.db.close()print("Database closed!")
這就是完整的數(shù)據(jù)庫的文件啦。因?yàn)樾【幍男枨髮?shù)據(jù)庫的操作不是很復(fù)雜,所以這些已基本滿足要求啦。注意下啦,在此之前,請朋友們先把pymysql裝起來!pymysql裝起來!pymysql裝起來!(重要的事情說三遍),安裝的方法很簡單,由于小編是使用pip來管理python包安裝的,所以只要進(jìn)入python安裝路徑下的pip文件夾下,執(zhí)行以下命令即可:
pip install pymysql
哈哈哈,這樣我們就可以利用python鏈接數(shù)據(jù)庫啦~(鼓個掌,慶祝下)
小伙伴們發(fā)現(xiàn)沒,在整個文件中,我們并沒有出現(xiàn)具體的變量值哦,為什么呢?沒錯,因?yàn)榍懊嫖覀儗懥薱onfig.ini文件,所有的數(shù)據(jù)庫配置信息都在這個文件內(nèi)哦,是不是感覺很方便呢,以后就算變更數(shù)據(jù)庫了,也只要修改config.ini文件的內(nèi)容就可以了,結(jié)合前面測試用例的管理(excel文件),sql語句的存放(xml文件),還有接下來我們要說的,businessCommon.py和存放具體case的文件夾,那么我們就已經(jīng)將數(shù)據(jù)和業(yè)務(wù)分開啦,哈哈哈,想想以后修改測試用例內(nèi)容,sql語句神馬的工作,再也不用每個case都修改,只要改幾個固定的文件,是不是頓時開心了呢?(嗯,想笑就大聲的笑吧)
回歸上面的configDB.py文件,內(nèi)容很簡單,相信大家都能看得懂,就是連接數(shù)據(jù)庫,執(zhí)行sql,獲取結(jié)果,最后關(guān)閉數(shù)據(jù)庫,沒有什么不一樣的地方。
該談?wù)勦]件啦,你是不是也遇到過這樣的問題:每次測試完之后,都需要給開發(fā)一份測試報告。那么,對于我這樣的懶人,是不愿意老是找人家開發(fā)的,所以,我就想,每次測試完,我們可以讓程序自己給開發(fā)人員發(fā)一封email,告訴他們,測試已經(jīng)結(jié)束了,并且把測試報告以附件的形式,通過email發(fā)送給開發(fā)者的郵箱,這樣豈不是爽哉!
所以,configEmail.py應(yīng)運(yùn)而生。當(dāng)當(dāng)當(dāng)當(dāng)……請看:
import os
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import datetime
import threading
import readConfig as readConfig
from common.Log import MyLog
import zipfile
import globlocalReadConfig = readConfig.ReadConfig()class Email:def __init__(self):global host, user, password, port, sender, title, contenthost = localReadConfig.get_email("mail_host")user = localReadConfig.get_email("mail_user")password = localReadConfig.get_email("mail_pass")port = localReadConfig.get_email("mail_port")sender = localReadConfig.get_email("sender")title = localReadConfig.get_email("subject")content = localReadConfig.get_email("content")self.value = localReadConfig.get_email("receiver")self.receiver = []# get receiver listfor n in str(self.value).split("/"):self.receiver.append(n)# defined email subjectdate = datetime.now().strftime("%Y-%m-%d %H:%M:%S")self.subject = title + " " + dateself.log = MyLog.get_log()self.logger = self.log.get_logger()self.msg = MIMEMultipart('mixed')def config_header(self):self.msg['subject'] = self.subjectself.msg['from'] = senderself.msg['to'] = ";".join(self.receiver)def config_content(self):content_plain = MIMEText(content, 'plain', 'utf-8')self.msg.attach(content_plain)def config_file(self):# if the file content is not null, then config the email fileif self.check_file():reportpath = self.log.get_result_path()zippath = os.path.join(readConfig.proDir, "result", "test.zip")# zip filefiles = glob.glob(reportpath + '\*')f = zipfile.ZipFile(zippath, 'w', zipfile.ZIP_DEFLATED)for file in files:f.write(file)f.close()reportfile = open(zippath, 'rb').read()filehtml = MIMEText(reportfile, 'base64', 'utf-8')filehtml['Content-Type'] = 'application/octet-stream'filehtml['Content-Disposition'] = 'attachment; filename="test.zip"'self.msg.attach(filehtml)def check_file(self):reportpath = self.log.get_report_path()if os.path.isfile(reportpath) and not os.stat(reportpath) == 0:return Trueelse:return Falsedef send_email(self):self.config_header()self.config_content()self.config_file()try:smtp = smtplib.SMTP()smtp.connect(host)smtp.login(user, password)smtp.sendmail(sender, self.receiver, self.msg.as_string())smtp.quit()self.logger.info("The test report has send to developer by email.")except Exception as ex:self.logger.error(str(ex))class MyEmail:email = Nonemutex = threading.Lock()def __init__(self):pass@staticmethoddef get_email():if MyEmail.email is None:MyEmail.mutex.acquire()MyEmail.email = Email()MyEmail.mutex.release()return MyEmail.emailif __name__ == "__main__":email = MyEmail.get_email()
這里就是完整的文件內(nèi)容了,不過可惜的是,小編我遇到一個問題,至今未解,在這里提出,希望大神給出解決辦法!跪求啦!
問題:使用163免費(fèi)郵箱服務(wù)器進(jìn)行郵件的發(fā)送,但是,每次發(fā)送郵件,都會被163郵件服務(wù)器退信,拋出的錯誤碼是:554?
官方說明如下:
?
但是,however,but……小編在整合email進(jìn)本框架之前寫的發(fā)送email的小demo是可以正常發(fā)送郵件的。這個問題困擾著我,目前仍沒有解決,望大神賜教。
離成功不遠(yuǎn)了,簡單說明下HTMLTestRunner.py文件,這個文件呢,也不是小編寫的,小編只是它的搬運(yùn)工,哈哈哈,這個文件是從網(wǎng)上下載的,大神寫好的,用于生成html格式的測試報告,什么?想知道生成測試報告的樣子?
看上去不錯吧,嗯,聰明的你們,也可以自己去探索下這個文件,修改修改,變成你自己的style哦~
好了,重頭戲來了,就是我們的runAll.py啦。請看主角登場。
這是我們整個框架運(yùn)行的入口,上面內(nèi)容完成后,這是最后一步啦,寫完它,我們的框架就算是完成了。(鼓掌,撒花~)
import unittest
import HTMLTestRunnerdef set_case_list(self):fb = open(self.caseListFile)for value in fb.readlines():data = str(value)if data != '' and not data.startswith("#"):self.caseList.append(data.replace("\n", ""))fb.close()def set_case_suite(self):self.set_case_list()test_suite = unittest.TestSuite()suite_model = []for case in self.caseList:case_file = os.path.join(readConfig.proDir, "testCase")print(case_file)case_name = case.split("/")[-1]print(case_name+".py")discover = unittest.defaultTestLoader.discover(case_file, pattern=case_name + '.py', top_level_dir=None)suite_model.append(discover)if len(suite_model) > 0:for suite in suite_model:for test_name in suite:test_suite.addTest(test_name)else:return Nonereturn test_suitedef run(self):try:suit = self.set_case_suite()if suit is not None:logger.info("********TEST START********")fp = open(resultPath, 'wb')runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='Test Report', description='Test Description')runner.run(suit)else:logger.info("Have no case to test.")except Exception as ex:logger.error(str(ex))finally:logger.info("*********TEST END*********")# send test report by emailif int(on_off) == 0:self.email.send_email()elif int(on_off) == 1:logger.info("Doesn't send report email to developer.")else:logger.info("Unknow state.")
上面我貼出了runAll里面的主要部分,首先我們要從caselist.txt文件中讀取需要執(zhí)行的case名稱,然后將他們添加到python自帶的unittest測試集中,最后執(zhí)行run()函數(shù),執(zhí)行測試集。關(guān)于python的unittest,需要學(xué)的內(nèi)容還是很多的,所以這里小編就不細(xì)講了
終于呢,整個接口自動化框架已經(jīng)講完了,大家是不是看明白了呢?什么?之前的之前貼出的目錄結(jié)構(gòu)中的文件還有沒說到的?嘿嘿,,,相信不用小編多說,大家也大概知道了,剩下文件夾的作用了。嗯~思索萬千,還是決定簡單談?wù)劙伞V苯由蠄D,簡單明了:
?
? ?
result文件夾會在首次執(zhí)行case時生成,并且以后的測試結(jié)果都會被保存在該文件夾下,同時每次測試的文件夾都是用系統(tǒng)時間命名,里面包含了兩個文件,log文件和測試報告。
?
testCase文件夾下,存放我們寫的具體的測試case啦,上面這些就是小編寫的一些。注意嘍,所有的case名稱都要以test開頭來命名哦,這是因?yàn)?#xff0c;unittest在進(jìn)行測試時會自動匹配testCase文件夾下面所有test開頭的.py文件
?
? ?testFile文件夾下,放置我們測試時用來管理測試用例的excel文件和用于數(shù)據(jù)庫查詢的sql語句的xml文件哦。
最后就是caselist.txt文件了,就讓你們瞄一眼吧:
?
凡是沒有被注釋掉的,都是要被執(zhí)行的case名稱啦。在這里寫上你要執(zhí)行的case名稱就可以啦。
Python接口自動化測試零基礎(chǔ)入門到精通(2023最新版)