python--接口自動化

日誌文件css

import logging
from logging import handlers
from conf.setting import LOG_PATH


class Logger(object):
# 日誌級別關係映射
level_relations = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'crit': logging.CRITICAL
}

def __init__(self, filename, level='info', when='D', back_count=3,
fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
self.logger = logging.getLogger(filename)
self.logger.setLevel(self.level_relations.get(level)) # 設置日誌級別
format_str = logging.Formatter(fmt)
sh = logging.StreamHandler()
sh.setFormatter(format_str)
th = handlers.TimedRotatingFileHandler(filename=filename, when=when,
backupCount=back_count, encoding='utf-8')
th.setFormatter(format_str)
self.logger.addHandler(sh)
self.logger.addHandler(th)


log = Logger(LOG_PATH, level='debug').logger # 直接在本文件實例化,之後導入log就行,不須要實例化了

if __name__ == '__main__':
log = Logger('nhy.log')
log.logger.debug('i的是100')
log.logger.info('開機')
log.logger.warning('警告 飛機沒油了')
log.logger.error('錯誤 飛機要爆炸')


報告文件
import time
from conf.setting import REPORT_PATH
import os


class HtmlReport(object):
__style_html = '''
<style type="text/css">
body {
font:normal 68% verdana,arial,helvetica;
color:#000000;
}
table tr td, table tr th {
font-size: 68%;
}
table.details tr th{
color: #ffffff;
font-weight: bold;
text-align:center;
background:#2674a6;
}
table.details tr td{
background:#eeeee0;
}
h1 {
margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
}
h2 {
margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
}
h3 {
margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
}
.Failure {
font-weight:bold; color:red;
}


img
{
border-width: 0px;
}

.expand_link
{
position=absolute;
right: 0px;
width: 27px;
top: 1px;
height: 27px;
}

.page_details
{
display: none;
}

.page_details_expanded
{
display: block;
display/* hide this definition from IE5/6 */: table-row;
}


</style>
<script language="JavaScript">
function show(details_id)
{
var close = 'page_details';
var show = 'page_details_expanded';
if (document.getElementById(details_id).className==close){
document.getElementById(details_id).className = show;
}
else {
document.getElementById(details_id).className = close;
}

}

</script>
'''
__report_html = '''
<!DOCTYPE html>
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>接口測試報告</title>
{style}
</head>
<body>
<h1>接口測試報告</h1>
<table width="100%">
<tr>
<td align="left">測試時間: {date}</td>
</tr>
</table>
<hr size="1">
<h2>測試概況</h2>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="details" align="center">
<tr valign="top">
<th>用例總數</th><th>經過數量</th><th>失敗數量</th><th>運行時間</th>
</tr>
<tr valign="top" class="">
<td align="center">{all}</td><td align="center">{ok}</td><td align="center">{fail}</td><td align="center">{run_time} s</td>
</tr>
</table>
<hr align="center" width="95%" size="1">
<h2>接口詳細</h2>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="details" align="center">
<tr valign="top">
<th>所屬項目</th><th>模塊</th><th>用例描述</th><th>URL</th><th>測試人員</th><th>用例狀態</th><th></th>
</tr>{case_res}</table>
<hr align="center" width="95%" size="1">
</body>
</html>

'''
__case_html = '''
<tr valign="top" class="">
<td>{project}</td><td align="center">{model}</td><td align="center">{detail}</td><td align="center">{url}</td><td align="center">{tester}</td><td align="center">{status}</td><td align="center"><a href="#" onclick="show('page_details_{case_id}');">查看接口詳細</a></td>
</tr>
<tr class="page_details" id="page_details_{case_id}">
<td bgcolor="#FF0000" colspan="8">
<div align="center">
<b>請求/返回 "{project}"</b>
<table width="95%" cellspacing="1" cellpadding="1" border="0" bgcolor="#2674A6" bordercolor="#000000">
<tr>
<th>請求報文</th><th>返回報文</th>
</tr>
<tr>
<td align="center" style="width :300px;word-break: break-all;"><span>{request}</span></td><td align="center" style="width :300px;word-break: break-all;" ><span>{response}</span></td>

</tr>
</table>
</div>
</td>
</tr>

'''

def __init__(self, report_dic):
'''

:param report_dic:生成報告須要用的字典
{
"all": 5,#運行用例數量
"ok": 4,#經過數量
"fail": 1,#失敗數量
"run_time": 100,#運行時間,單位s
"case_res": [{}],#每條用例的執行結果,
case_res:
{
"case_id":"001",#用例id
"project":"易品",#所屬項目
"model":"登陸",#模塊
"detail":"正常登陸",#用例標題
"url":"http://10.165.124.28:8080/q", #請求url
"tester":"牛牛", #測試人員
"status":"經過",#測試結果
"request":"a=1&b=2",#請求報文
"response":"{'code':200,'msg':'操做成功'}"#返回報文
}
}
'''
self.report_dic = report_dic

def report(self):
res_list_html = ''
res_list = self.report_dic.get('case_res')
for res in res_list:
res_list_html += self.__case_html.format(**res)
self.report_dic['case_res'] = res_list_html
self.report_dic['style'] = self.__style_html
self.report_dic['date'] = time.strftime('%Y/%m/%d %H:%M:%S')
self.__write_file()

def __write_file(self):
self.file_name = os.path.join(REPORT_PATH, '{date}_TestReport.html'.format(date=time.strftime('%Y%m%d%H%M%S')))
with open(self.file_name, 'w', encoding='utf-8') as fw:
fw.write(self.__report_html.format(**self.report_dic))


if __name__ == '__main__':
res_list = [
{
"case_id": "1",
"project": "易品",
"model": "登陸",
"detail": "正常登陸",
"url": "http://10.165.124.28:8080/q",
"tester": "牛牛",
"status": "經過",
"request": "a=1&b=2",
"response": "{'code':200,'msg':'操做成功'}"
}
]

all = {
"all": 5,
"ok": 4,
"fail": 1,
"run_time": 100,
"case_res": res_list,
"date": time.strftime('%Y/%m/%d %H:%M:%S')
}

a = HtmlReport(all)
a.report()



發送郵件文件
import smtplib
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import base64


# qq受權碼
# lfuhiziuplykbgdd
# vavqwyhdvjdzcabh
class SendMail(object):
def __init__(self, username, passwd, recv, title, content,
file=None, ssl=False,
email_host='smtp.126.com', port=25, ssl_port=465):
# :param username: 用戶名
# :param passwd: 密碼
# :param recv: 收件人,多個要傳list ['a@qq.com','b@qq.com]
# :param title: 郵件標題
# :param content: 郵件正文
# :param file: 附件路徑,若是不在當前目錄下,要寫絕對路徑,默認沒有附件
# :param ssl: 是否安全連接,默認爲普通
# :param email_host: smtp服務器地址,默認爲163服務器
# :param port: 非安全連接端口,默認爲25
# :param ssl_port: 安全連接端口,默認爲465
self.username = username # 用戶名
self.passwd = passwd # 密碼
self.recv = recv # 收件人,多個要傳list ['a@qq.com','b@qq.com]
self.title = title # 郵件標題
self.content = content # 郵件正文
self.file = file # 附件路徑,若是不在當前目錄下,要寫絕對路徑
self.email_host = email_host # smtp服務器地址
self.port = port # 普通端口
self.ssl = ssl # 是否安全連接
self.ssl_port = ssl_port # 安全連接端口
# self.smtp = smtp

def send_mail(self):
msg = MIMEMultipart()
# 發送內容的對象
if self.file: # 處理附件的
file_name = os.path.split(self.file)[-1] # 只取文件名,不取路徑
try:
f = open(self.file, 'rb').read()
except Exception as e:
raise Exception('附件打不開!', e)
else:
att = MIMEText(f, "base64", "utf-8")
att["Content-Type"] = 'application/octet-stream'
# base64.b64encode(file_name.encode()).decode()
new_file_name = '=?utf-8?b?' + base64.b64encode(file_name.encode()).decode() + '?='
# 這裏是處理文件名爲中文名的,必須這麼寫
att["Content-Disposition"] = 'attachment; filename="%s"' % new_file_name
msg.attach(att)
msg.attach(MIMEText(self.content)) # 郵件正文的內容
msg['Subject'] = self.title # 郵件主題
msg['From'] = self.username # 發送者帳號
msg['To'] = ','.join(self.recv) # 接收者帳號列表
if self.ssl:
self.smtp = smtplib.SMTP_SSL(self.email_host, port=self.ssl_port)
else:
self.smtp = smtplib.SMTP(self.email_host, port=self.port)
# 發送郵件服務器的對象
self.smtp.login(self.username, self.passwd)
try:
self.smtp.sendmail(self.username, self.recv, msg.as_string())
pass
except Exception as e:
print('出錯了。。', e)
else:
print('發送成功!')
self.smtp.quit()


if __name__ == '__main__':
m = SendMail(
username='389688559@qq.com',
passwd='lfuhiziuplykbgdd',
recv=['wangsilei@126.com', '511402865@qq.com'],
title='過年好',
content='新年快樂',
file=r'C:\Users\Wang\Desktop\新年快樂.txt',
ssl=True,
)
m.send_mail()



工具文件,含多種方法
import xlrd
import os
from common.MyLog import log
import requests
from xlutils.copy import copy
import time
from common.發送郵件最終版 import SendMail
from conf.setting import MAIL_USER_INFO, MAIL_PASSWORD, RECV


def read_case(case_path): # 讀用例
if os.path.isfile(case_path):
book = xlrd.open_workbook(case_path)
sheet = book.sheet_by_index(0)
all_case = [] # 全部的測試用例
for row in range(1, sheet.nrows):
all_case.append(sheet.row_values(row)[:8])
log.debug('用例信息爲:%s' % all_case)
log.info('總共讀取了%s條測試用例' % len(all_case))

return all_case
else:
log.error('讀取用例不存在,用例路徑爲%s' % case_path)
raise Exception('用例不存在!')


class MyRequest(object): # 調接口
@classmethod
def post(cls, url, data):
data = cls.str_to_dict(data)
try:
res = requests.post(url, data=data).text
except Exception as e:
log.error('接口調用出錯,錯誤信息爲%s,url爲%s' % (e, url))
res = e
return res, data

@classmethod
def get(cls, url, data):
data = cls.str_to_dict(data)
try:
res = requests.get(url, data=data).text
except Exception as e:
log.error('接口調用出錯,錯誤信息爲%s,url爲%s' % (e, url))
res = e
return res, data

@classmethod
def str_to_dict(cls, st: str):
# a=1&b=2&c=3
# 1. &分割 ['a=1', 'b=2', 'c=3']
# 2. ['a', '1']
res = {}
if st.strip():
for d in st.split('&'):
j = d.split('=')
res[j[0]] = j[1]
return res


def check_res(expected, res): # 校驗結果
# error_code=0,userId=1
"""
{
"error_code": 0,
"login_info": {
"login_time": "20180311155222",
"sign": "4beba93fceae97c997f40fd424696691",
"userId": 1
}
}
"""
new_res = res.replace('": "', '=').replace('": ', '=')
for c in expected.split(','):
# ['error_code=0', 'userId=1']
if c not in new_res:
return '失敗'
return '經過'


def write_excel(case_path, all_res): # 結果寫回excel
# all_res存放全部的測試結果
book1 = xlrd.open_workbook(case_path)
book2 = copy(book1) # 拷貝一份原來的
sheet = book2.get_sheet(0) # 獲取第幾個sheet頁
line = 1 # 行號
for res in all_res:
sheet.write(line, 8, str(res[0]))
sheet.write(line, 9, res[1])
sheet.write(line, 10, res[2])
sheet.write(line, 11, res[3])
line += 1
book2.save(case_path)


# all_res = [
# ['{"usrname":"niuhanyang"}', '{xxxx}', '經過', '原寶青'],
# ['{"usrname":"niuhanyang"}', '{xxxx}', '經過', '原寶青'],
# ['{"usrname":"niuhanyang"}', '{xxxx}', '經過', '原寶青']
#
# ]
# write_excel(r'C:\Users\wangsilei\Desktop\測試用例.xls', all_res)


def send_report(all_count, pass_count, report_file=None): # 發送測試報告
if report_file:
title = time.strftime('%Y-%m-%d %H:%M:%S') + '接口測試報告'
content = '''
你們好!
本次接口測試,共運行{all}條測試用例
經過{ok}條
失敗{fail}條
測試報告詳細信息見附件!
'''.format(all=all_count, ok=pass_count, fail=(all_count - pass_count))
m = SendMail(MAIL_USER_INFO, MAIL_PASSWORD, RECV, title, content, file=report_file)
m.send_mail()



run文件
import os
import sys
import time

from common import tools
from conf.setting import CASE_PATH, TESTER
from common.tools import MyRequest
from common.report import HtmlReport

BASE_PATH = os.path.dirname(
os.path.dirname(
os.path.abspath(__file__)
)
)
sys.path.insert(0, BASE_PATH)


class RunCase(object):
def find_case(self):
for case_file in os.listdir(CASE_PATH):
if case_file.endswith('.xls'):
# 獲取用例
self.case_path = os.path.join(CASE_PATH, case_file)
all_case = tools.read_case(self.case_path)
self.run_case(all_case)

def run_case(self, all_case):
excel_res = [] # 寫入excel的結果使用的
report_res = {} # 給生成的函數使用的
case_res = [] # 存放每一個用例的結果
pass_count = 0 # 經過的次數
for case in all_case:
project = case[0]
model = case[1]
case_id = case[2]
case_detail = case[3]
url = case[4]
method = case[5]
req_data = case[6]
check = case[7]
if method.upper() == 'POST':
response, data = MyRequest.post(url, req_data)
else:
response, data = MyRequest.get(url, req_data)
case_status = tools.check_res(check, response) # 獲取用例執行結果
pass_count = pass_count + 1 if case_status == '經過' else pass_count
tmp_case_res = self.create_report(case_id, project, model, case_detail, url, TESTER, case_status, data, response)
case_res.append(tmp_case_res)
excel_res.append([data, response, case_status, TESTER]) # 把每條用例的結果加入到寫excel的那個裏面
tools.write_excel(self.case_path, excel_res)
all_cases_num = len(all_case)
report_res['all'] = all_cases_num # 總共的用例數
report_res['ok'] = pass_count # 經過的數量
report_res['fail'] = all_cases_num - pass_count # 失敗的數量
report_res['case_res'] = case_res # 每條用例的結果
report_res['date'] = time.strftime('%Y-%m-%d %H:%M:%S')
report_res['run_time'] = 1
report_obj = HtmlReport(report_res) # 實例化對象
report_obj.report() # 生成報告
tools.send_report(all_cases_num, pass_count, report_obj.file_name) # 發送測試報告
print('測試運行完成。。。')

def create_report(self, case_id, project, model, detail, url, tester, status, request, response):
return {
'case_id': case_id,
'project': project,
'model': model,
'detail': detail,
'url': url,
'tester': tester,
'status': status,
'request': request,
'response': response,
}

run = RunCase()
run.find_case()



配置文件
import osBASE_PATH = os.path.dirname(    os.path.dirname(        os.path.abspath(__file__)    ))LOG_PATH = os.path.join(BASE_PATH, 'logs', 'atp.log')  # 日誌路徑REPORT_PATH = os.path.join(BASE_PATH, 'report')  # 報告路徑CASE_PATH = os.path.join(BASE_PATH, 'cases')  # 用例的路徑MAIL_USER_INFO = 'wangsilei@126.com'MAIL_PASSWORD = 'xxxxxxxx'RECV = ['389688559@qq.com', '356367764@qq.com']TESTER = '王思磊'
相關文章
相關標籤/搜索