簡書已停更付翁,歡迎轉(zhuǎn)到個人博客查看對應(yīng)教程:https://www.cnblogs.com/superhin/p/10339002.html
課程目錄
Python接口測試實(shí)戰(zhàn)1(上)- 接口測試?yán)碚?/a>
Python接口測試實(shí)戰(zhàn)1(下)- 接口測試工具的使用
Python接口測試實(shí)戰(zhàn)2 - 使用Python發(fā)送請求
Python接口測試實(shí)戰(zhàn)3(上)- Python操作數(shù)據(jù)庫
Python接口測試實(shí)戰(zhàn)3(下)- unittest測試框架
Python接口測試實(shí)戰(zhàn)4(上) - 接口測試框架實(shí)戰(zhàn)
Python接口測試實(shí)戰(zhàn)4(下) - 框架完善:用例基類箩张,用例標(biāo)簽小渊,重新運(yùn)行上次失敗用例
Python接口測試實(shí)戰(zhàn)5(上) - Git及Jenkins持續(xù)集成
Python接口測試實(shí)戰(zhàn)5(下) - RESTful、Web Service及Mock Server
更多學(xué)習(xí)資料請加添加作者微信:superz-han獲取
本節(jié)內(nèi)容
- 數(shù)據(jù)分離 - 從Excel中讀取數(shù)據(jù)
- 增加log功能
- 發(fā)送郵件
- 使用配置文件
- 框架整理
數(shù)據(jù)分離 - 從Excel中讀取數(shù)據(jù)
之前的用例中坏为,數(shù)據(jù)直接寫在代碼文件里,不利于修改和構(gòu)造數(shù)據(jù)
這里我們使用Excel保存測試數(shù)據(jù),實(shí)現(xiàn)代碼和數(shù)據(jù)的分離
新建Excel文件test_user_data.xlsx
包含兩個工作簿TestUserLogin
和TestUserReg
逛腿,并復(fù)制到項(xiàng)目根目錄下
更新: excel表格中,增加一個headers列,內(nèi)容為json格式, 如下
TestUserLogin
case_name | url | method | headers | data | expect_res |
---|---|---|---|---|---|
test_user_login_normal | http://115.28.108.130:5000/api/user/login/ | POST | {} | {"name": "張三","password":"123456"} | <h1>登錄成功</h1> |
test_user_login_password_wrong | http://115.28.108.130:5000/api/user/login/ | POST | {} | {"name": "張三","password":"1234567"} | <h1>失敗,用戶名或密碼錯誤</h1> |
TestUserReg
case_name | url | method | headers | data | expect_res |
---|---|---|---|---|---|
test_user_reg_normal | http://115.28.108.130:5000/api/user/login/ | POST | {} | {"name": "范冰冰","password":"123456"} | "{"code: "100000","msg": "成功,"data": {""name": "范冰冰,"password": "e10adc3949ba59abbe56e057f20f883e"}}" |
test_user_reg_exist | http://115.28.108.130:5000/api/user/login/ | POST | {} | {"name": "張三","password":"123456"} | "{"code": "100001","msg": "失敗仅颇,用戶已存在","data": {"name": "張三","password":"e10adc3949ba59abbe56e057f20f883e"}}" |
Excel讀取方法:
Python我們使用三方庫xlrd來讀取Excel
安裝方法: pip install xlrd
import xlrd
wb = xlrd.open_workbook("test_user_data.xlsx") # 打開excel
sh = wb.sheet_by_name("TestUserLogin") # 按工作簿名定位工作表
print(sh.nrows) # 有效數(shù)據(jù)行數(shù)
print(sh.ncols) # 有效數(shù)據(jù)列數(shù)
print(sh.cell(0, 0).value) # 輸出第一行第一列的值`case_name`
print(sh.row_values(0)) # 輸出第1行的所有值(列表格式)
# 將數(shù)據(jù)和標(biāo)題組裝成字典单默,使數(shù)據(jù)更清晰
print(dict(zip(sh.row_values(0), sh.row_values(1))))
# 遍歷excel,打印所有的數(shù)據(jù)
for i in range(sh.nrows):
print(sh.row_values(i))
結(jié)果:
3
5
case_name
['case_name', 'url', 'method', 'data', 'expect_res']
{'case_name': 'test_user_login_normal', 'url': 'http://115.28.108.130:5000/api/user/login/', 'method': 'POST', 'data': '{"name": "張三","password":"123456"}', 'expect_res': '<h1>登錄成功</h1>'}
['case_name', 'url', 'method', 'data', 'expect_res']
['test_user_login_normal', 'http://115.28.108.130:5000/api/user/login/', 'POST', '{"name": "張三","password":"123456"}', '<h1>登錄成功</h1>']
['test_user_login_password_wrong', 'http://115.28.108.130:5000/api/user/login/', 'POST', '{"name": "張三","password":"1234567"}', '<h1>失敗,用戶不存在</h1>']
封裝讀取excel操作:
新建read_excel.py
我們的目的是獲取某條用例的數(shù)據(jù)灵莲,需要3個參數(shù)雕凹,excel數(shù)據(jù)文件名(data_file),工作簿名(sheet),用例名(case_name)
如果我們只封裝一個函數(shù)枚抵,每次調(diào)用(每條用例)都要打開一次excel并遍歷一次线欲,這樣效率比較低。
我們可以拆分成兩個函數(shù)汽摹,一個函數(shù)excel_to_list(data_file, sheet)
李丰,一次獲取一個工作表的所有數(shù)據(jù),另一個函數(shù)get_test_data(data_list, case_name)
從所有數(shù)據(jù)中去查找到該條用例的數(shù)據(jù)逼泣。
import xlrd
def excel_to_list(data_file, sheet):
data_list = [] # 新建個空列表趴泌,來乘裝所有的數(shù)據(jù)
wb = xlrd.open_workbook(data_file) # 打開excel
sh = wb.sheet_by_name(sheet) # 獲取工作簿
header = sh.row_values(0) # 獲取標(biāo)題行數(shù)據(jù)
for i in range(1, sh.nrows): # 跳過標(biāo)題行,從第二行開始取數(shù)據(jù)
d = dict(zip(header, sh.row_values(i))) # 將標(biāo)題和每行數(shù)據(jù)組裝成字典
data_list.append(d)
return data_list # 列表嵌套字典格式拉庶,每個元素是一個字典
def get_test_data(data_list, case_name):
for case_data in data_list:
if case_name == case_data['case_name']: # 如果字典數(shù)據(jù)中case_name與參數(shù)一致
return case_data
# 如果查詢不到會返回None
if __name__ == '__main__': # 測試一下自己的代碼
data_list = excel_to_list("test_user_data.xlsx", "TestUserLogin") # 讀取excel嗜憔,TestUserLogin工作簿的所有數(shù)據(jù)
case_data = get_test_data(data_list, 'test_user_login_normal') # 查找用例'test_user_login_normal'的數(shù)據(jù)
print(case_data)
輸出結(jié)果:
{'case_name': 'test_user_login_normal', 'url': 'http://115.28.108.130:5000/api/user/login/', 'method': 'POST', 'data': '{"name": "張三","password":"123456"}', 'expect_res': '<h1>登錄成功</h1>'}
用例中使用方法
test_user_login.py 部分
import unittest
import requests
from read_excel import * # 導(dǎo)入read_excel中的方法
import json # 用來轉(zhuǎn)化excel中的json字符串為字典
class TestUserLogin(unittest.TestCase):
@classmethod
def setUpClass(cls): # 整個測試類只執(zhí)行一次
cls.data_list = excel_to_list("test_user_data.xlsx", "TestUserLogin") # 讀取該測試類所有用例數(shù)據(jù)
# cls.data_list 同 self.data_list 都是該類的公共屬性
def test_user_login_normal(self):
case_data = get_test_data(self.data_list, 'test_user_login_normal') # 從數(shù)據(jù)列表中查找到該用例數(shù)據(jù)
if not case_data: # 有可能為None
print("用例數(shù)據(jù)不存在")
url = case_data.get('url') # 從字典中取數(shù)據(jù),excel中的標(biāo)題也必須是小寫url
data = case_data.get('data') # 注意字符串格式氏仗,需要用json.loads()轉(zhuǎn)化為字典格式
expect_res = case_data.get('expect_res') # 期望數(shù)據(jù)
res = requests.post(url=url, data=json.loads(data)) # 表單請求吉捶,數(shù)據(jù)轉(zhuǎn)為字典格式
self.assertEqual(res.text, expect_res) # 改為assertEqual斷言
if __name__ == '__main__': # 非必要,用于測試我們的代碼
unittest.main(verbosity=2)
test_user_reg.py部分
import unittest
import requests
from db import *
from read_excel import *
import json
class TestUserReg(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.data_list = excel_to_list("test_user_data.xlsx", "TestUserReg") # 讀取TestUserReg工作簿的所有數(shù)據(jù)
def test_user_reg_normal(self):
case_data = get_test_data(self.data_list, 'test_user_reg_normal')
if not case_data:
print("用例數(shù)據(jù)不存在")
url = case_data.get('url')
data = json.loads(case_data.get('data')) # 轉(zhuǎn)為字典皆尔,需要取里面的name進(jìn)行數(shù)據(jù)庫檢查
expect_res = json.loads(case_data.get('expect_res')) # 轉(zhuǎn)為字典呐舔,斷言時直接斷言兩個字典是否相等
name = data.get("name") # 范冰冰
# 環(huán)境檢查
if check_user(name):
del_user(name)
# 發(fā)送請求
res = requests.post(url=url, json=data) # 用data=data 傳字符串也可以
# 響應(yīng)斷言(整體斷言)
self.assertDictEqual(res.json(), expect_res)
# 數(shù)據(jù)庫斷言
self.assertTrue(check_user(name))
# 環(huán)境清理(由于注冊接口向數(shù)據(jù)庫寫入了用戶信息)
del_user(name)
if __name__ == '__main__': # 非必要,用于測試我們的代碼
unittest.main(verbosity=2)
增加log功能
新建config.py文件
import logging
logging.basicConfig(level=logging.DEBUG, # log level
format='[%(asctime)s] %(levelname)s [%(funcName)s: %(filename)s, %(lineno)d] %(message)s', # log格式
datefmt='%Y-%m-%d %H:%M:%S', # 日期格式
filename='log.txt', # 日志輸出文件
filemode='a') # 追加模式
if __name__ == '__main__':
logging.info("hello")
運(yùn)行后在當(dāng)前目錄下生成log.txt
慷蠕,內(nèi)容如下:
[2018-09-11 18:08:17] INFO [<module>: config.py, 38] hello
Log Level:
- CRITICAL: 用于輸出嚴(yán)重錯誤信息
- ERROR: 用于輸出錯誤信息
- WARNING: 用于輸出警示信息
- INFO: 用于輸出一些提升信息
- DEBUG: 用于輸出一些調(diào)試信息
優(yōu)先級 CRITICAL > ERROR > WARNING > INFO > DEBUG
指定level = logging.DEBUG
所有等級大于等于DEBUG的信息都會輸出
若指定level = logging.ERROR
WARNING,INFO,DEBUG小于設(shè)置級別的信息不會輸出
日志格式:
- %(levelno)s: 打印日志級別的數(shù)值
- %(levelname)s: 打印日志級別名稱
- %(pathname)s: 打印當(dāng)前執(zhí)行程序的路徑珊拼,其實(shí)就是sys.argv[0]
- %(filename)s: 打印當(dāng)前執(zhí)行程序名
- %(funcName)s: 打印日志的當(dāng)前函數(shù)
- %(lineno)d: 打印日志的當(dāng)前行號
- %(asctime)s: 打印日志的時間
- %(thread)d: 打印線程ID
- %(threadName)s: 打印線程名稱
- %(process)d: 打印進(jìn)程ID
- %(message)s: 打印日志信息
項(xiàng)目使用log
將所有print改為log,如db.py
部分
import pymysql
from config import *
# 封裝數(shù)據(jù)庫查詢操作
def query_db(sql):
conn = get_db_conn()
cur = conn.cursor()
logging.debug(sql) # 輸出執(zhí)行的sql
cur.execute(sql)
conn.commit()
result = cur.fetchall()
logging.debug(result) # 輸出查詢結(jié)果
cur.close()
conn.close()
return result
# 封裝更改數(shù)據(jù)庫操作
def change_db(sql):
conn = get_db_conn()
cur = conn.cursor()
logging.debug(sql) # 輸出執(zhí)行的sql
try:
cur.execute(sql)
conn.commit()
except Exception as e:
conn.rollback()
logging.error(str(e)) # 輸出錯誤信息
finally:
cur.close()
conn.close()
用例中使用
import unittest
import requests
from read_excel import * # 導(dǎo)入read_excel中的方法
import json # 用來轉(zhuǎn)化excel中的json字符串為字典
from config import *
class TestUserLogin(unittest.TestCase):
@classmethod
def setUpClass(cls): # 整個測試類只執(zhí)行一次
cls.data_list = excel_to_list("test_user_data.xlsx", "TestUserLogin") # 讀取該測試類所有用例數(shù)據(jù)
# cls.data_list 同 self.data_list 都是該類的公共屬性
def test_user_login_normal(self):
case_data = get_test_data(self.data_list, 'test_user_login_normal') # 從數(shù)據(jù)列表中查找到該用例數(shù)據(jù)
if not case_data: # 有可能為None
logging.error("用例數(shù)據(jù)不存在")
url = case_data.get('url') # excel中的標(biāo)題也必須是小寫url
data = case_data.get('data') # 注意字符串格式流炕,需要用json.loads()轉(zhuǎn)化為字典格式
expect_res = case_data.get('expect_res') # 期望數(shù)據(jù)
res = requests.post(url=url, data=json.loads(data)) # 表單請求澎现,數(shù)據(jù)轉(zhuǎn)為字典格式
logging.info("測試用例:{}".format('test_user_login_normal'))
logging.info("url:{}".format(url))
logging.info("請求參數(shù):{}".format(data))
logging.info("期望結(jié)果:{}".format(expect_res))
logging.info("實(shí)際結(jié)果:{}".format(res.text)
self.assertEqual(res.text, expect_res) # 斷言
if __name__ == '__main__':
unittest.main(verbosity=2)
項(xiàng)目下log.txt
輸出結(jié)果:
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 8] 測試用例:test_user_login_normal
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 9] url:http://115.28.108.130:5000/api/user/login/
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 10] 請求參數(shù):{"name": "張三","password":"123456"}
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 11] 期望結(jié)果:<h1>登錄成功</h1>
[2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 12] 實(shí)際結(jié)果:<h1>登錄成功</h1>
因?yàn)槊總€用例都要輸出很多l(xiāng)og信息,我們封裝一個case_log
的函數(shù)
項(xiàng)目下新建case_log.py
from config import *
import json
def log_case_info(case_name, url, data, expect_res, res_text):
if isinstance(data,dict):
data = json.dumps(data, ensure_ascii=False) # 如果data是字典格式浪感,轉(zhuǎn)化為字符串
logging.info("測試用例:{}".format(case_name))
logging.info("url:{}".format(url))
logging.info("請求參數(shù):{}".format(data))
logging.info("期望結(jié)果:{}".format(expect_res))
logging.info("實(shí)際結(jié)果:{}".format(res_text)
簡化后的用例log輸出
import unittest
import requests
from read_excel import *
import json
from config import *
from case_log import log_case_info # 導(dǎo)入方法
class TestUserLogin(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.data_list = excel_to_list("test_user_data.xlsx", "TestUserLogin")
def test_user_login_normal(self):
case_data = get_test_data(self.data_list, 'test_user_login_normal')
if not case_data:
logging.error("用例數(shù)據(jù)不存在")
url = case_data.get('url')
data = case_data.get('data')
expect_res = case_data.get('expect_res')
res = requests.post(url=url, data=json.loads(data))
log_case_info('test_user_login_normal', url, data, expect_res, res_text) # 輸出用例log信息
self.assertEqual(res.text, expect_res)
if __name__ == '__main__':
unittest.main(verbosity=2)
發(fā)送郵件
在生成報告后我們希望框架能自動把報告發(fā)送到我們的郵箱中昔头。和outlook,foxmail等郵件客戶端一樣影兽,Python中發(fā)送郵件需要通過Email的smtp服務(wù)發(fā)送揭斧。
首先需要確認(rèn)用來發(fā)送郵件的郵箱是否啟用了smtp服務(wù)
發(fā)送郵件分3步
- 編寫郵件內(nèi)容(Email郵件需要專門的MIME格式)
- 組裝Email頭(發(fā)件人,收件人峻堰,主題)
- 連接smtp服務(wù)器并發(fā)送郵件
import smtplib # 用于建立smtp連接
from email.mime.text import MIMEText # 郵件需要專門的MIME格式
# 1. 編寫郵件內(nèi)容(Email郵件需要專門的MIME格式)
msg = MIMEText('this is a test email', 'plain', 'utf-8') # plain指普通文本格式郵件內(nèi)容
# 2. 組裝Email頭(發(fā)件人讹开,收件人,主題)
msg['From'] = 'test_results@sina.com' # 發(fā)件人
msg['To'] = '2375247815@qq.com' # 收件人
msg['Subject'] = 'Api Test Report' # 郵件主題
# 3. 連接smtp服務(wù)器并發(fā)送郵件
smtp = smtplib.SMTP_SSL('smtp.sina.com') # smtp服務(wù)器地址 使用SSL模式
smtp.login('自己的郵箱地址', '自己的郵箱密碼') # 用戶名和密碼
smtp.sendmail("接收郵件地址1", "接收郵件地址2", msg.as_string())
smtp.quit()
中文郵件主題捐名、HTML郵件內(nèi)容旦万,及附件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart # 混合MIME格式,支持上傳附件
from email.header import Header # 用于使用中文郵件主題
# 1. 編寫郵件內(nèi)容
with open('report.html', encoding='utf-8') as f: # 打開html報告
email_body = f.read() # 讀取報告內(nèi)容
msg = MIMEMultipart() # 混合MIME格式
msg.attach(MIMEText(email_body, 'html', 'utf-8')) # 添加html格式郵件正文(會丟失css格式)
# 2. 組裝Email頭(發(fā)件人镶蹋,收件人成艘,主題)
msg['From'] = 'test_results@sina.com' # 發(fā)件人
msg['To'] = '2375247815@qq.com' # 收件人
msg['Subject'] = Header('接口測試報告', 'utf-8') # 中文郵件主題赏半,指定utf-8編碼
# 3. 構(gòu)造附件1,傳送當(dāng)前目錄下的 test.txt 文件
att1 = MIMEText(open('report.html', 'rb').read(), 'base64', 'utf-8') # 二進(jìn)制格式打開
att1["Content-Type"] = 'application/octet-stream'
att1["Content-Disposition"] = 'attachment; filename="report.html"' # filename為郵件中附件顯示的名字
msg.attach(att1)
# 4. 連接smtp服務(wù)器并發(fā)送郵件
smtp = smtplib.SMTP_SSL('smtp.sina.com') # smtp服務(wù)器地址 使用SSL模式
smtp.login('test_results@sina.com', 'hanzhichao123') # 用戶名和密碼
smtp.sendmail("test_results@sina.com", "2375247815@qq.com", msg.as_string())
smtp.sendmail("test_results@sina.com", "superhin@126.com", msg.as_string()) # 發(fā)送給另一個郵箱
smtp.quit()
封裝發(fā)送郵件方法
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart # 混合MIME格式淆两,支持上傳附件
from email.header import Header # 用于使用中文郵件主題
from config import *
def send_email(report_file):
msg = MIMEMultipart() # 混合MIME格式
msg.attach(MIMEText(open(report_file, encoding='utf-8').read(), 'html', 'utf-8')) # 添加html格式郵件正文(會丟失css格式)
msg['From'] = 'test_results@sina.com' # 發(fā)件人
msg['To'] = '2375247815@qq.com' # 收件人
msg['Subject'] = Header('接口測試報告', 'utf-8') # 中文郵件主題断箫,指定utf-8編碼
att1 = MIMEText(open(report_file, 'rb').read(), 'base64', 'utf-8') # 二進(jìn)制格式打開
att1["Content-Type"] = 'application/octet-stream'
att1["Content-Disposition"] = 'attachment; filename="report.html"' # filename為郵件中附件顯示的名字
msg.attach(att1)
try:
smtp = smtplib.SMTP_SSL('smtp.sina.com') # smtp服務(wù)器地址 使用SSL模式
smtp.login('test_results@sina.com', 'hanzhichao123') # 用戶名和密碼
smtp.sendmail("test_results@sina.com", "2375247815@qq.com", msg.as_string())
smtp.sendmail("test_results@sina.com", "superhin@126.com", msg.as_string()) # 發(fā)送給另一個郵箱
logging.info("郵件發(fā)送完成!")
except Exception as e:
logging.error(str(e))
finally:
smtp.quit()
run_all.py中結(jié)束后發(fā)送郵件
import unittest
from HTMLTestReportCN import HTMLTestRunner
from config import *
from send_email import send_email
logging.info("================================== 測試開始 ==================================")
suite = unittest.defaultTestLoader.discover("./")
with open("report.html", 'wb') as f: # 改為with open 格式
HTMLTestRunner(stream=f, title="Api Test", description="測試描述", tester="卡卡").run(suite)
send_email('report.html') # 發(fā)送郵件
logging.info("================================== 測試結(jié)束 ==================================")
使用配置文件
和項(xiàng)目的log配置一樣秋冰,數(shù)據(jù)庫服務(wù)器地址仲义,郵件服務(wù)地址我們一般放到配置文件config.py
中
import logging
import os
# 項(xiàng)目路徑
prj_path = os.path.dirname(os.path.abspath(__file__)) # 當(dāng)前文件的絕對路徑的上一級,__file__指當(dāng)前文件
data_path = prj_path # 數(shù)據(jù)目錄剑勾,暫時在項(xiàng)目目錄下
test_path = prj_path # 用例目錄埃撵,暫時在項(xiàng)目目錄下
log_file = os.path.join(prj_path, 'log.txt') # 也可以每天生成新的日志文件
report_file = os.path.join(prj_path, 'report.html') # 也可以每次生成新的報告
# log配置
logging.basicConfig(level=logging.DEBUG, # log level
format='[%(asctime)s] %(levelname)s [%(funcName)s: %(filename)s, %(lineno)d] %(message)s', # log格式
datefmt='%Y-%m-%d %H:%M:%S', # 日期格式
filename=log_file, # 日志輸出文件
filemode='a') # 追加模式
# 數(shù)據(jù)庫配置
db_host = '127.0.0.1' # 自己的服務(wù)器地址
db_port = 3306
db_user = 'test'
db_passwd = '123456'
db = 'api_test'
# 郵件配置
smtp_server = 'smtp.sina.com'
smtp_user = 'test_results@sina.com'
smtp_password = 'hanzhichao123'
sender = smtp_user # 發(fā)件人
receiver = '2375247815@qq.com' # 收件人
subject = '接口測試報告' # 郵件主題
修改db.py
,send_email.py
,run_all.py
等對配置文件的引用
db.py
部分
import pymysql
from config import *
# 獲取連接方法
def get_db_conn():
conn = pymysql.connect(host=db_host, # 從配置文件中讀取
port=db_port,
user=db_user,
passwd=db_passwd, # passwd 不是 password
db=db,
charset='utf8') # 如果查詢有中文虽另,需要指定測試集編碼
send_email.py
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from config import *
def send_email(report_file):
msg = MIMEMultipart()
msg.attach(MIMEText(open(report_file, encoding='utf-8').read(), 'html', 'utf-8'))
msg['From'] = 'test_results@sina.com'
msg['To'] = '2375247815@qq.com'
msg['Subject'] = Header(subject, 'utf-8') # 從配置文件中讀取
att1 = MIMEText(open(report_file, 'rb').read(), 'base64', 'utf-8') # 從配置文件中讀取
att1["Content-Type"] = 'application/octet-stream'
att1["Content-Disposition"] = 'attachment; filename="{}"'.format(report_file) # 參數(shù)化一下report_file
msg.attach(att1)
try:
smtp = smtplib.SMTP_SSL(smtp_server) # 從配置文件中讀取
smtp.login(smtp_user, smtp_password) # 從配置文件中讀取
smtp.sendmail(sender, receiver, msg.as_string())
logging.info("郵件發(fā)送完成暂刘!")
except Exception as e:
logging.error(str(e))
finally:
smtp.quit()
run_all.py
import unittest
from HTMLTestReportCN import HTMLTestRunner
from config import *
from send_email import send_email
logging.info("================================== 測試開始 ==================================")
suite = unittest.defaultTestLoader.discover(test_path) # 從配置文件中讀取用例路徑
with open(report_file, 'wb') as f: # 從配置文件中讀取
HTMLTestRunner(stream=f, title="Api Test", description="測試描述").run(suite)
send_email(report_file) # 從配置文件中讀取
logging.info("================================== 測試結(jié)束 ==================================")
框架整理
當(dāng)前所有文件(配置文件,公共方法捂刺,測試用例鸳惯,數(shù)據(jù),報告叠萍,log)都在項(xiàng)目根目錄下,隨著用例的增加和功能的補(bǔ)充绪商,文件會越來越多苛谷,不便于維護(hù)和管理,因此我們要建立不同的文件夾格郁,對文件進(jìn)行分類組織
- 在項(xiàng)目中新建以下文件夾:
- config: 存放項(xiàng)目配置文件
- data: 存放用例數(shù)據(jù)文件
- lib: 公共方法庫
- log: 存放日志文件
- report: 存放報告文件
- test: 存放測試用例
- user: 存放user模塊用例 (模塊下要有
__init__.py
腹殿,這樣里面的用例才能讀取到)
- user: 存放user模塊用例 (模塊下要有
- 將配置文件
config.py
移動到config目錄下,將數(shù)據(jù)文件test_user_data.xlsx
移動到data目錄下例书,將公共方法db.py
send_email.py
case_log.py
read_excel.py
HTMLTestReportCN.py
移動到lib目錄下锣尉,將測試用例test_user_login.py
test_user_reg.py
移動到test/user目錄下,保留run_all.py
在項(xiàng)目根目錄下决采,如圖:
整理后的項(xiàng)目結(jié)構(gòu) - 修改配置文件
config/config.py
部分
import logging
import os
# 項(xiàng)目路徑
prj_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 當(dāng)前文件的上一級的上一級目錄(增加一級)
data_path = os.path.join(prj_path, 'data') # 數(shù)據(jù)目錄
test_path = os.path.join(prj_path, 'test') # 用例目錄
log_file = os.path.join(prj_path, 'log', 'log.txt') # 更改路徑到log目錄下
report_file = os.path.join(prj_path, 'report', 'report.html') # 更改路徑到report目錄下
- 修改對配置文件及公共方法的引用
為避免相對路徑導(dǎo)包出錯的問題自沧,我們統(tǒng)一把導(dǎo)包搜索路徑(sys.path)提升到項(xiàng)目根目錄下,如
lib/db.py
lib/db.py
部分
import pymysql
import sys
sys.path.append('..') # 提升一級到項(xiàng)目更目錄下
from config.config import * # 從項(xiàng)目根目錄下導(dǎo)入
測試用例test_user_login.py
部分
import unittest
import requests
import json
import os # 增加了一個os树瞭,需要用來組裝路徑
import sys
sys.path.append("../..") # 提升2級到項(xiàng)目根目錄下
from config.config import * # 從項(xiàng)目路徑下導(dǎo)入
from lib.read_excel import * # 從項(xiàng)目路徑下導(dǎo)入
from lib.case_log import log_case_info # 從項(xiàng)目路徑下導(dǎo)入
class TestUserLogin(unittest.TestCase):
@classmethod
def setUpClass(cls): # 整個測試類只執(zhí)行一次
cls.data_list = excel_to_list(os.path.join(data_path, "test_user_data.xlsx"),"TestUserLogin") # 增加data路徑
run_all.py
import unittest
from lib.HTMLTestReportCN import HTMLTestRunner # 修改導(dǎo)入路徑
from config.config import * # 修改導(dǎo)入路徑
from lib.send_email import send_email # 修改導(dǎo)入路徑
logging.info("================================== 測試開始 ==================================")
suite = unittest.defaultTestLoader.discover(test_path) # 從配置文件中讀取
with open(report_file, 'wb') as f: # 從配置文件中讀取
HTMLTestRunner(stream=f, title="Api Test", description="測試描述").run(suite)
send_email(report_file) # 從配置文件中讀取
logging.info("================================== 測試結(jié)束 ==================================")
- 如果同一文件夾下的方法相互引用(如
lib/read_excel.py
假如需要引用lib/db.py
)拇厢,也需要采用這種從項(xiàng)目路徑下導(dǎo)入的方式run_all.py
直接在項(xiàng)目路徑下,不需要提升sys.path晒喷,無需相對導(dǎo)入我們自己的包時孝偎,如read_excel.py
,不需要提升
- 運(yùn)行
run_all.py
凉敲,根據(jù)log和報告調(diào)試代碼衣盾,直至所有用例全部通過
源碼下載鏈接:https://pan.baidu.com/s/1RzwAlUMHwG4FQmeS-yB9rw 密碼:rvq1