1、通用模块:

  • config.conf: 公共配置文件,配置报告、日志、截图路径,以及邮件相关配置
  • [report]
    reportpath = E:\workspace\WebAutomation\src\functiontest\Report\2017-07-18
    screen_path = E:\workspace\WebAutomation\src\functiontest\Report\2017-07-18\Screenshoots
    report_path = E:\workspace\WebAutomation\src\functiontest\Report\2017-07-18\Report\TestReport-2017-07-18-15-23-06.html
    log_path = E:\workspace\WebAutomation\src\functiontest\Report\2017-07-18\Logs\2017-07-18-15-23-06.log [mail]
    mail_from = xxx
    mail_tolist = xxx
    mail_host = mail.xx.com
    mail_user = xxxx
    mail_pass = aGFpbmFuNVU1Ng==
  • logger: 日志模块

  • main.py: 执行器,负责执行整体测试任务模块

  • testrunner.py: 负责测试用例执行和结果收集

  • utils.py: 公共方法,如创建报告文件夹、生成测试报告、发送邮件

2、日志模块:

#coding:utf-8
import logging.handlers
import ConfigParser class Loger(logging.Logger):
def __init__(self, filename=None):
super(Loger, self).__init__(self)
# 日志文件名
conf = ConfigParser.ConfigParser()
conf.read("config.conf")
filename = conf.get("report", "log_path")
self.filename = filename # 创建一个handler,用于写入日志文件
fh = logging.handlers.RotatingFileHandler(self.filename, 'a')
fh.setLevel(logging.DEBUG) # 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG) # 定义handler的输出格式
formatter_fh = logging.Formatter('[%(asctime)s] - %(filename)s [Line:%(lineno)d] - [%(levelname)s] - %(message)s')
formatter_ch = logging.Formatter('[%(asctime)s] - %(message)s')
fh.setFormatter(formatter_fh)
ch.setFormatter(formatter_ch) # 给logger添加handler
self.addHandler(fh)
#self.addHandler(ch)

3、执行模块:

# coding=utf-8
import unittest
import os
import Utils
from sys import argv def runTest(case_dir, patter):
import TestRunner
reload(TestRunner)
discover = unittest.defaultTestLoader.discover(case_dir+"\\testcases", pattern=patter)
runner = TestRunner.AutoTestRunner()
result, infos = runner.run(discover)
return result, infos def run(cadir):
filename = Utils.createFolder(cadir[0]) #创建文件夹
import Logger
log = Logger.Loger()
log.info(cadir[2] + u"测试开始")
log.info(u"开始创建文件夹和文件")
log.info(u"日志文件:"+filename[0])
log.info(u"报告文件:"+filename[1])
log.info(u"文件夹和文件创建成功")
log.info(u"开始执行测试用例")
result, infos = runTest(cadir[0], cadir[1]) #收集和执行测试用例
log.info(u"测试用例执行完成,开始写入报告")
if cadir[2] == "functiontest":
Utils.createReport(result, infos, filename, cadir[3]) #测试结果写入报告
log.info(u"报告写入结束,测试结束")
log.info(u"开始发送邮件……")
isSuccess = Utils.sendMail(filename[1],cadir[3])
log.info(isSuccess)
log.info("================================================================\n") if __name__ == '__main__':
projectpath = os.path.dirname(os.path.realpath(__file__))
test_dir = projectpath + '\\functiontest\\' #功能测试用例路径
test_dir1 = projectpath + '\\interfacetest\\' #接口测试用例路径
casedirs = []
#argv=["","all"]
if argv[1] == "interface":
casedirs.append([test_dir1, "*TestCase.py", "interfacetest","接口"])
elif argv[1] == "function":
casedirs.append([test_dir, "TestCase*.py", "functiontest","功能"])
else:
casedirs.append([test_dir1, "*TestCase.py", "interfacetest","接口"])
casedirs.append([test_dir, "TestCase*.py", "functiontest","功能"]) for cadir in casedirs:
run(cadir)

4、创建报告文件夹:

"""创建报告文件夹、日志文件夹、截图文件夹、日志文件、报告文件"""
def createFolder(test_path):
# conf = ConfigParser.ConfigParser()
# conf.read("config.conf")
# reportFolder = conf.get("result", "resultpath") + time.strftime('%Y-%m-%d', time.localtime(time.time()))
reportFolder = test_path + "Report\\" + time.strftime('%Y-%m-%d', time.localtime(time.time()))
log_path = reportFolder + "\\Logs"
screen_path = reportFolder + "\\Screenshoots"
report_path = reportFolder + "\\Report"
pathlist = [report_path, log_path, screen_path]
for paths in pathlist:
if os.path.exists(paths):
pass
else:
os.makedirs(paths)
logFile = log_path + "\\" + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time())) + ".log"
f = open(logFile, 'a')
f.close() reportname = report_path + "\\TestReport-" + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time())) + ".html"
f = open(reportname, 'w') htmlStr = '''
<html>
<head>
<meta charset='utf-8' />
<style>
body{counter-reset:num;}
li{list-style: none;text-indent:10px;}
li:after{content: counter(num);counter-increment:num;}
pre {
white-space: pre-wrap;
word-wrap: break-word;
display:block;
padding:5px;
font-size:13px;
color:#333;
background-color:#f5f5f5;
border:1px solid #ccc;
border-radius:4px;
font-family:'Consolas';
} h1 {
font-size: 16pt;
color: gray;
}
.heading {
margin-top: 0ex;
margin-bottom: 1ex;
} .heading .attribute {
margin-top: 1ex;
margin-bottom: 0;
} .heading .description {
margin-top: 2ex;
margin-bottom: 3ex;
} /* -- table --- */
table {
border-collapse: collapse; /* IE7 and lower */
border-spacing: 0;
width: 100%;
} .bordered {
border: solid #ccc 1px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 1px 1px #ccc;
-moz-box-shadow: 0 1px 1px #ccc;
box-shadow: 0 1px 1px #ccc; } .bordered td {
border: 1px solid #ccc;
padding: 5px;
font-size:14px;
} .header_row {
font-weight: bold;
background-color: #dce9f9;
} /* -- css div popup --- */
a {
color:#428bca;
text-decoration:none;
} a:hover {
text-decoration:underline;
} .popup_window {
display: none;
/*position: relative;*/
/*border: solid #627173 1px; */
padding: 10px;
background-color: #E6E6D6;
text-align: left;
font-size: 8pt;
width: 500px;
} .hiddenRow {
display: none;
} .displayRow {
display: block;
}
.testcase { margin-left: 2em; color: #000; font-weight: bold;} /* -- report -- */
#show_detail_line {
margin-top: 3ex;
margin-bottom: 1ex;
} </style> <script language="javascript" type="text/javascript">
function showCase(level) {
trs = document.getElementsByTagName("tr");
for (var i = 0; i < trs.length; i++) {
tr = trs[i];
id = tr.id;
if (id.substr(0,2) == 'ft') {
if (level == 0) {
tr.className = 'none';
}
if (level == 1) {
tr.className = 'hiddenRow';
}
if (level == 2) {
tr.className = 'none';
}
if (level == 3) {
tr.className = 'hiddenRow';
}
}
if (id.substr(0,2) == 'pt') {
if (level == 0) {
tr.className = 'none';
}
if (level == 1) {
tr.className = 'none';
}
if (level == 2) {
tr.className = 'hiddenRow';
}
if (level == 3) {
tr.className = 'hiddenRow';
}
}
if (id.substr(0,2) == 'st') {
if (level == 0) {
tr.className = 'none';
}
if (level == 1) {
tr.className = 'hiddenRow';
}
if (level == 2) {
tr.className = 'hiddenRow';
}
if (level == 3) {
tr.className = 'none';
}
}
}
} function showTestDetail(tr_id){
table = document.getElementById('result_table')
trs = table.getElementsByTagName("tr");
for (var i = 0; i < trs.length; i++) {
tr = trs[i];
id = tr.id;
if ((id.split('_'))[1] == tr_id){
trn = document.getElementById(id)
if (trn.className == 'none' ) {
trn.className = 'hiddenRow';
}
else {
trn.className = 'none';
}
} }
} function showFailDetail(div_id){
var details_div = document.getElementById(div_id)
var displayState = details_div.style.display
if (displayState != 'block' ) {
displayState = 'block'
details_div.style.display = 'block'
}
else {
details_div.style.display = 'none'
}
} </script>
</head><body style='font-family:微软雅黑'>
''' f.write(htmlStr)
f.close()
conf = ConfigParser.ConfigParser()
conf.read("config.conf")
conf.set("report", "reportpath", reportFolder)
conf.set("report", "screen_path", screen_path)
conf.set("report", "log_path", logFile)
conf.set("report", "report_path", reportname)
conf.write(open("config.conf", "w")) conf.read(test_path + "\\TestCases\\config.conf")
conf.set("report", "reportpath", reportFolder)
conf.set("report", "screen_path", screen_path)
conf.set("report", "log_path", logFile)
conf.set("report", "report_path", reportname)
conf.write(open("config.conf", "w"))
filenames = [logFile, reportname]
return filenames

5、创建创建功能测试报告

"""创建html格式的测试报告"""
def createReport(t_result, t_info, filename, reporttype):
#localMachine = socket.getfqdn(socket.gethostname())
#localIP = socket.gethostbyname(localMachine)
f = open(filename[1], 'a') unskip = (t_info)["CaseNum"]-(t_info)["Skip"]
passrate = (float((t_info)["Success"]) / (float(unskip))) * 100
htmlstr = '''
<h3>执行概述</h3>
<p style='font-size:12px;'>点击各数字可以筛选对应结果的用例。</p>
<table class='bordered' style='width:1100px; text-align:center'>
<tr class='header_row'>
<td style='width:100px'>用例总数</td>
<td style='width:100px'>通过</td>
<td style='width:100px'>失败</td>
<td style='width:100px'>跳过</td>
<td style='width:100px'>错误</td>
<td style='width:100px'>通过率</td>
<td>开始时间</td><td>运行时间</td><td>日志文件</td></tr>
<tr><td><a href='javascript:showCase(0)'>%s</a></td>
<td><a href='javascript:showCase(1)'>%s</a></td>
<td><a href='javascript:showCase(2)'>%s</a></td>
<td><a href='javascript:showCase(3)'>%s</a></td>
<td><a href='javascript:showCase(2)'>%s</a></td>
<td>%.2f%%</td><td>%s</td><td>%s</td>
<td><a href='%s'>%s</a></td>
</tr></table>
''' % ((t_info)["CaseNum"],(t_info)["Success"], (t_info)["Fail"], (t_info)["Skip"], (t_info)["Error"],passrate, \
(t_info)["StartTime"],(t_info)["TakeTime"],filename[0], os.path.split(filename[0])[-1])
f.write(htmlstr) htmlstr = '''
<h3>执行详情</h3>
<p style='font-size:12px;'>Pass:通过,Failed:失败,Skip:跳过,Error:错误。点击Failed可以查看错误详情。</p>
<table id='result_table' class="bordered">
<tr class='header_row'>
<td>编号</td>
<td style='width:300px'>测试用例</td>
<td style='width:300px'>中文描述</td>
<td>耗时</td>
<td style='width:300px'>测试结果</td>
<td>查看</td>
</tr>
'''
f.write(htmlstr) i=1
j=1
for key in t_result.results.keys():
htmlstr = '''
<tr><td colspan='6' class='testcase'>
<a class="popup_link" onfocus="this.blur();" href="javascript:showTestDetail('%s')">%s</a></td></tr>
''' % (str(j),key)
f.write(htmlstr) value = t_result.results[key]
count=1
for key1 in value.keys():
takentime = ((value[key1])["stoptime"] - (value[key1])["starttime"]).seconds
takentime = str(datetime.timedelta(seconds=takentime))
if (value[key1])["Result"] == "Failed" or (value[key1])["Result"] == "Error":
htmlstr = "<tr id='ft_%s_%s' class='none' style='color:red'>" % (str(j),str(count))
f.write(htmlstr) htmlstr = '''
<td><li></li></td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>
<a class="popup_link" style='color:red;text-decoration:underline;' onfocus='this.blur();' href="javascript:showFailDetail('div_ft%s')" >%s</a>
<div id='div_ft%s' class="popup_window">
<div style='text-align: right; color:red;cursor:pointer'>
<a onfocus='this.blur();' onclick="document.getElementById('div_ft%s').style.display = 'none' " >X</a></div>
<pre>%s</pre></td>
<td><a href='%s' target='_blank'>查看截图</a></td></tr>
''' % (key1, (value[key1])["name"], takentime, str(i),(value[key1])["Result"], str(i),str(i),(value[key1])["Reason"], (value[key1])["Screenshoot"])
f.write(htmlstr)
i+=1 elif (value[key1])["Result"] == "Pass":
htmlstr = '''
<tr id='pt_%s_%s' class='none'>
<td><li></li></td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td style='color:green;'>%s</td>
<td></td></tr>
''' % ((str(j), str(count), key1, (value[key1])["name"], takentime, (value[key1])["Result"]))
f.write(htmlstr) else:
htmlstr = '''
<tr id='st_%s_%s' class='none'>
<td><li></li></td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td></td></tr>
''' % ((str(j), str(count), key1, (value[key1])["name"], takentime, (value[key1])["Result"]))
f.write(htmlstr)
count += 1 j+=1
f.write("</table></body></html>")
f.close()

6、创建接口测试报告

"""创建html格式的接口测试报告"""
def createInterfaceReport(resultlist, t_info, reportfile, logfile):
#localMachine = socket.getfqdn(socket.gethostname())
#localIP = socket.gethostbyname(localMachine)
f = open(reportfile, 'a') passrate = (float((t_info)["pass"]) / float((t_info)["total"])) * 100
htmlstr = '''
<h3>执行概述</h3>
<table class='bordered' style='width:1000px; text-align:center'>
<tr class='header_row'>
<td style='width:100px'>用例总数</td>
<td style='width:100px'>通过</td>
<td style='width:100px'>失败</td>
<td style='width:100px'>通过率</td>
<td>开始时间</td><td>运行时间</td><td>日志文件</td></tr>
<tr><td><a href='javascript:showCase(0)'>%s</a></td>
<td><a href='javascript:showCase(1)'>%s</a></td>
<td><a href='javascript:showCase(2)'>%s</a></td>
<td>%.2f%%</td>
<td>%s</td>
<td>%.3fs</td>
<td><a href='%s'>%s</a></td>
</tr></table>
''' % ((t_info)["total"], (t_info)["pass"], (t_info)["fail"], passrate,t_info['starttime'], t_info['takentime'], logfile, os.path.split(logfile)[-1])
f.write(htmlstr) htmlstr = '''
<h3>执行详情</h3>
<table id='result_table' class="bordered">
<tr class='header_row'>
<td>编号</td>
<td>模块</td>
<td>用例描述</td>
<td>接口</td>
<td>参数</td>
<td>请求结果</td>
<td>测试结果</td>
<td>耗时(毫秒)</td>
</tr>
'''
f.write(htmlstr) i=1
for result in resultlist:
if result[5] == "Failed":
htmlstr = "<tr id='ft_%s' class='none' style='color:red'>" % str(i)
else:
htmlstr = "<tr id='pt_%s' class='none' style='color:green'>" % str(i)
htmlstr += '''
<td><li></li></td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td style='word-wrap:break-word; max-width:500px;'>%s</td>
<td>%s</td>
<td>%s ms</td></tr>
''' % (result[0], result[1], result[2], result[3], result[4], result[5], result[6])
f.write(htmlstr.encode('utf-8'))
i+=1 f.write("</table></body></html>")
f.close()

7、发送邮件

import ConfigParser
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import datetime
import base64
#import socket """发送邮件"""
def sendMail(reportname, reporttype):
conf = ConfigParser.ConfigParser()
conf.read("config.conf")
mail_from = conf.get("mail", "mail_from") # 发件箱
mail_tolist = conf.get("mail", "mail_tolist") # 收件人列表
mail_host = conf.get("mail", "mail_host") # 服务器
mail_user = conf.get("mail", "mail_user") # 用户名
mail_pass = conf.get("mail", "mail_pass") # 密码
mail_pass = base64.decodestring(mail_pass) f = open(reportname, 'r')
content = f.read()
msg = MIMEMultipart()
puretext = MIMEText(content, _subtype='html', _charset='utf-8') # 设置html格式邮件
htmlpart = MIMEApplication(open(reportname, 'rb').read())
htmlpart.add_header('Content-Disposition', 'attachment', filename=os.path.basename(reportname))
msg.attach(puretext)
msg.attach(htmlpart)
sub = reporttype + "自动化测试报告-" + time.strftime("%Y/%m/%d", time.localtime(time.time()))
msg['Subject'] = sub # 设置主题
msg['From'] = mail_from
msg['To'] = mail_tolist
sendmailinfo = ""
try:
s = smtplib.SMTP()
s.connect(mail_host) # 连接smtp服务器
s.login(mail_user, mail_pass) # 登陆服务器
s.sendmail(mail_from, mail_tolist, msg.as_string()) # 发送邮件
s.close()
sendmailinfo = "邮件发送成功!"
except Exception, e:
sendmailinfo = "邮件发送失败,错误信息:" + str(e)
return sendmailinfo

Python web功能&接口自动化测试框架搭建——接口用例实现

Python web功能&接口自动化测试框架搭建——接口公共方法

Python web功能&接口自动化测试框架搭建——接口测试模块

Python web功能&接口自动化测试框架搭建——功能测试模块

Python web功能&接口自动化测试框架搭建——测试用例执行和结果收集

Python web功能&接口自动化测试框架搭建——通用模块

Python web功能&接口自动化测试框架搭建——unittest介绍

Python web功能&接口自动化测试框架搭建——环境搭建

python web自动化测试框架搭建(功能&接口)——通用模块的更多相关文章

  1. python web自动化测试框架搭建(功能&接口)——接口测试模块

    Python接口测试采用python读取excel的方法,通过requests库发送请求和接收响应.模块有: Data:用于存放excel用例的,用例格式: iutil: 接口公共方法,数据引擎.ht ...

  2. python web自动化测试框架搭建(功能&接口)——功能测试模块

    功能测试使用selenium,模块有: 1.futil: 公共方法,如元素高亮显示 # coding=utf-8 """高亮显示元素""" ...

  3. python web自动化测试框架搭建(功能&接口)——接口用例实现

    测试用例基类: # coding=utf-8 import unittest import Logger log = Logger.Loger() class BaseCase(unittest.Te ...

  4. python web自动化测试框架搭建(功能&接口)——接口公共方法

    接口公共方法有:数据引擎.http引擎.Excel引擎 1.数据引擎:获取用例.结果检查.结果统计 # -*- coding:utf-8 -*- from XlsEngine import XlsEn ...

  5. python web自动化测试框架搭建(功能&接口)——测试用例执行和结果收集

    由于unittest框架中结果收集在不同文件中,所以此处重写结果收集方法,加入执行时间,失败信息,失败截图等 TestRunner.py # coding=utf-8 import sys impor ...

  6. Python web自动化测试框架搭建(功能&接口)——unittest介绍

    Python UnitTest测试框架介绍 1)         TestCase:所有测试用例类继承的基本类, TestCase的实例就是测试用例 2)         TestSuite:测试套件 ...

  7. python web自动化测试框架搭建(功能&接口)——环境搭建

    自动化测试框架一般需要实现以下通用功能 执行前准备 结束后清理 执行步骤输出 执行结果输出 错误.失败截图 测试报告 发送邮件 日志 需要的软件和python第三方库有: 通用: JDK Eclips ...

  8. 基于python的自动化测试框架搭建

    滴~ 今日打卡!   好多天没来打卡了.博主最近一直在把碎片化知识转化为知识体系的过程中挣扎.Python语言.selenium.unittest框架.HTMLTestRunner框架都有所了解,也写 ...

  9. selenium +python web自动化测试环境搭建

    基础框架搭建 1.安装python 2.安装selenium cmd输入pip install selenium 问题:在python中输入from selenium import webdriver ...

随机推荐

  1. Linux 查看日志文件

    1. tail命令:从文本文件的尾部开始查看,用于显示文本文件的末尾几行      tail -n filename    指定需要显示多少行          tail -f filename 实时 ...

  2. [Java] 歐付寶金流串接教學

    前言: 很多接案的人,都會碰到需要接金流的時候.而歐付寶是個台灣的金流平台. 這邊記錄下,串接的心得.我用的語言是Java, 採liferay這個portal平台,不過這份教學當然適合servlet. ...

  3. Ubuntu 下使用 python3 制作读取 QR 码

    Ubuntu 下使用 python3 制作读取 QR 码 作者已经在 Windows 上实现 python3 制作读取 QR 码.本文主要针对解决将代码移植到 Ubuntu 系统时所遇到的问题. 相关 ...

  4. td内容超出 以…显示

    table中的td内容超出以省略号显示,需满足的条件是: <style type="text/css"> table{ table-layout: fixed; bor ...

  5. vue 中使用style(样式)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. struts2导入多个xml引入报错<include>

    struts.xml <?xml version="1.0" encoding="UTF-8"?> <!-- 指定Struts 2配置文件的D ...

  7. 2018-8-10-使用-Resharper-特性

    title author date CreateTime categories 使用 Resharper 特性 lindexi 2018-08-10 19:16:51 +0800 2018-4-25 ...

  8. Firewalld--03 富规则、备份恢复、开启内部上网

    目录 防火墙富规则.备份恢复.开启内部上网 1. 防火墙富规则策略 2.Firewalld备份恢复 3. 防火墙开启内部上网 防火墙富规则.备份恢复.开启内部上网 1. 防火墙富规则策略 ​ Fire ...

  9. numpy中tile的用法

    a=arange(1,3) #a的结果是: array([1,2]) 1,当 tile(a,1) 时: tile(a,1) #结果是 array([1,2]) tile(a,2) #结果是 array ...

  10. Java 多态概念、使用

    1.概念 2.多态的格式与使用 package Java12; /* 代码当中体现多态性,其实就是一句话: 父类引用指向子类对象 格式: 父类名称 对象名 = new 子类名称(): 或者: 接口名称 ...