营销型网站结构图,注册造价工程师,淘客cms建站系统,淮安做网站公司目录
八、测试套件TestSuite和测试运行器TestRunner
1、基本概念
2、创建和使用测试套件
3、 自动发现测试用例、创建测试套件、运行测试
4、生成html的测试报告#xff1a;HTMLTestRunner
1️⃣导入HTMLTestRunner模块
2️⃣运行测试用例并生成html文件
九、unittest…目录
八、测试套件TestSuite和测试运行器TestRunner
1、基本概念
2、创建和使用测试套件
3、 自动发现测试用例、创建测试套件、运行测试
4、生成html的测试报告HTMLTestRunner
1️⃣导入HTMLTestRunner模块
2️⃣运行测试用例并生成html文件
九、unittest测试框架的设计与实现
1、对项目重构
2、处理excel表的测试数据
十、使用邮件接收测试结果 八、测试套件TestSuite和测试运行器TestRunner
测试套件Test Suite是用于组织和管理多个测试用例Test Case的容器它允许你将不同模块中多个测试用例集合在一起并一起执行。测试运行器TestRunner用于执行 TestSuite 中的所有测试用例并将结果以文本的形式输出到控制台。 TestSuite 和 TextTestRunner 通常是配合使用的。TestSuite 组织和管理测试用例TextTestRunner 执行这些测试用例并输出测试结果。
1、基本概念
测试用例Test Case是指单个的测试方法它验证了某个功能或行为是否符合预期。测试套件Test Suite是由多个测试用例组成的集合。它可以包含多个测试用例也可以包含其他测试套件从而形成一个嵌套的结构。测试运行器TestRunnerTextTestRunner 会接收一个 TestSuite 对象并执行其中的所有测试用例。它会在控制台中输出详细的测试结果包括每个测试用例的执行情况通过、失败、错误等。另外通过自定义 TestResult 类TextTestRunner 也可以支持更详细或不同格式的输出。
2、创建和使用测试套件
在 unittest 中测试套件主要通过 unittest.TestSuite 类来创建。你可以通过以下两种方式来创建和执行测试套件①手动将测试用例添加到测试套件②自动发现并添加测试用例。
3、 自动发现测试用例、创建测试套件、运行测试
unittest 提供了一个 discover() 方法可以自动发现符合特定命名规则的测试用例并将它们组织成一个测试套件。它会递归地查找指定目录下的所有测试模块并将它们自动加载到测试套件中。
在当前项目中创建main.py文件 可以看到当前目录有很多测试类 import unittest# 自动发现当前目录下的所有测试模块并运行
suite unittest.TestSuite()
case unittest.defaultTestLoader.discover(start_dir.,patterntest*.py)suite.addTest(case)# 创建测试运行器执行测试套件是固定的使用方法
runner unittest.TextTestRunner()
runner.run(suite)
discover() 方法unittest.defaultTestLoader.discover() 会从指定的 start_dir 目录开始递归查找所有符合 pattern 模式的测试文件。默认情况下文件名应该以 test_ 开头后面跟 .py 后缀。执行测试套件通过 unittest.TextTestRunner() 执行自动发现的测试套件。
4、生成html的测试报告HTMLTestRunner
unittest自带的测试报告是文本格式的可读性不强所以一般使用第三方模块来生成测试报告。
HTMLTestRunner 是 Python unittest 框架的一个第三方扩展用于将测试结果以 HTML 格式输出而不是标准的文本输出。它提供了一个漂亮且直观的报告适用于需要以网页形式查看测试结果的场景。
1️⃣导入HTMLTestRunner模块
HTMLTestRunner模块下载不能通过pip安装需要新建新建 HTMLTestRunner.py 文件将网页内容复制到文件中即可。 A TestRunner for use with the Python unit testing framework. It
generates a HTML report to show the result at a glance.The simplest way to use this is to invoke its main method. E.g.import unittestimport HTMLTestRunner... define your tests ...if __name__ __main__:HTMLTestRunner.main()For more customization options, instantiates a HTMLTestRunner object.
HTMLTestRunner is a counterpart to unittests TextTestRunner. E.g.# output to a filefp file(my_report.html, wb)runner HTMLTestRunner.HTMLTestRunner(streamfp,titleMy unit test,descriptionThis demonstrates the report output by HTMLTestRunner.)# Use an external stylesheet.# See the Template_mixin class for more customizable optionsrunner.STYLESHEET_TMPL link relstylesheet hrefmy_stylesheet.css typetext/css# run the testrunner.run(my_test_suite)------------------------------------------------------------------------
Copyright (c) 2004-2007, Wai Yip Tung
All rights reserved.Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:* Redistributions of source code must retain the above copyright notice,this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyrightnotice, this list of conditions and the following disclaimer in thedocumentation and/or other materials provided with the distribution.
* Neither the name Wai Yip Tung nor the names of its contributors may beused to endorse or promote products derived from this software withoutspecific prior written permission.THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS
IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# URL: http://tungwaiyip.info/software/HTMLTestRunner.html__author__ Wai Yip Tung
__version__ 0.8.2
Change HistoryVersion 0.8.2
* Show output inline instead of popup window (Viorel Lupu).Version in 0.8.1
* Validated XHTML (Wolfgang Borgert).
* Added description of test classes and test cases.Version in 0.8.0
* Define Template_mixin class for customization.
* Workaround a IE 6 bug that it does not treat script block as CDATA.Version in 0.7.1
* Back port to Python 2.3 (Frank Horowitz).
* Fix missing scroll bars in detail log (Podi).
# TODO: color stderr
# TODO: simplify javascript using ,ore than 1 class in the class attribute?import datetime
import io
import sys
import time
import unittest
from xml.sax import saxutils# ------------------------------------------------------------------------
# The redirectors below are used to capture output during testing. Output
# sent to sys.stdout and sys.stderr are automatically captured. However
# in some cases sys.stdout is already cached before HTMLTestRunner is
# invoked (e.g. calling logging.basicConfig). In order to capture those
# output, use the redirectors for the cached stream.
#
# e.g.
# logging.basicConfig(streamHTMLTestRunner.stdout_redirector)
# class OutputRedirector(object): Wrapper to redirect stdout or stderr def __init__(self, fp):self.fp fpdef write(self, s):self.fp.write(s)def writelines(self, lines):self.fp.writelines(lines)def flush(self):self.fp.flush()stdout_redirector OutputRedirector(sys.stdout)
stderr_redirector OutputRedirector(sys.stderr)# ----------------------------------------------------------------------
# Templateclass Template_mixin(object):Define a HTML template for report customerization and generation.Overall structure of an HTML reportHTML------------------------|html || head || || STYLESHEET || ---------------- || | | || ---------------- || || /head || || body || || HEADING || ---------------- || | | || ---------------- || || REPORT || ---------------- || | | || ---------------- || || ENDING || ---------------- || | | || ---------------- || || /body ||/html |------------------------STATUS {0: pass,1: fail,2: error,}DEFAULT_TITLE Unit Test ReportDEFAULT_DESCRIPTION # ------------------------------------------------------------------------# HTML TemplateHTML_TMPL r?xml version1.0 encodingUTF-8?
!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Strict//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd
html xmlnshttp://www.w3.org/1999/xhtml
headtitle%(title)s/titlemeta namegenerator content%(generator)s/meta http-equivContent-Type contenttext/html; charsetUTF-8/%(stylesheet)s
/head
body
script languagejavascript typetext/javascript!--
output_list Array();/* level - 0:Summary; 1:Failed; 2:All */
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 1) {tr.className hiddenRow;}else {tr.className ;}}if (id.substr(0,2) pt) {if (level 1) {tr.className ;}else {tr.className hiddenRow;}}}
}function showClassDetail(cid, count) {var id_list Array(count);var toHide 1;for (var i 0; i count; i) {tid0 t cid.substr(1) . (i1);tid f tid0;tr document.getElementById(tid);if (!tr) {tid p tid0;tr document.getElementById(tid);}id_list[i] tid;if (tr.className) {toHide 0;}}for (var i 0; i count; i) {tid id_list[i];if (toHide) {document.getElementById(div_tid).style.display nonedocument.getElementById(tid).className hiddenRow;}else {document.getElementById(tid).className ;}}
}function showTestDetail(div_id){var details_div document.getElementById(div_id)var displayState details_div.style.display// alert(displayState)if (displayState ! block ) {displayState blockdetails_div.style.display block}else {details_div.style.display none}
}function html_escape(s) {s s.replace(//g,amp;);s s.replace(//g,lt;);s s.replace(//g,gt;);return s;
}/* obsoleted by detail in div
function showOutput(id, name) {var w window.open(, //urlname,resizable,scrollbars,status,width800,height450);d w.document;d.write(pre);d.write(html_escape(output_list[id]));d.write(\n);d.write(a hrefjavascript:window.close()close/a\n);d.write(/pre\n);d.close();
}
*/
--/script%(heading)s
%(report)s
%(ending)s/body
/html
# variables: (title, generator, stylesheet, heading, report, ending)# ------------------------------------------------------------------------# Stylesheet## alternatively use a link for external style sheet, e.g.# link relstylesheet href$url typetext/cssSTYLESHEET_TMPL
style typetext/css mediascreen
body { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
table { font-size: 100%; }
pre { }/* -- heading ---------------------------------------------------------------------- */
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: 4ex;margin-bottom: 6ex;
}/* -- css div popup ------------------------------------------------------------------------ */
a.popup_link {
}a.popup_link:hover {color: red;
}.popup_window {display: none;position: relative;left: 0px;top: 0px;/*border: solid #627173 1px; */padding: 10px;background-color: #E6E6D6;font-family: Lucida Console, Courier New, Courier, monospace;text-align: left;font-size: 8pt;width: 500px;
}}
/* -- report ------------------------------------------------------------------------ */
#show_detail_line {margin-top: 3ex;margin-bottom: 1ex;
}
#result_table {width: 80%;border-collapse: collapse;border: 1px solid #777;
}
#header_row {font-weight: bold;color: white;background-color: #777;
}
#result_table td {border: 1px solid #777;padding: 2px;
}
#total_row { font-weight: bold; }
.passClass { background-color: #6c6; }
.failClass { background-color: #c60; }
.errorClass { background-color: #c00; }
.passCase { color: #6c6; }
.failCase { color: #c60; font-weight: bold; }
.errorCase { color: #c00; font-weight: bold; }
.hiddenRow { display: none; }
.testcase { margin-left: 2em; }/* -- ending ---------------------------------------------------------------------- */
#ending {
}/style
# ------------------------------------------------------------------------# Heading#HEADING_TMPL div classheading
h1%(title)s/h1
%(parameters)s
p classdescription%(description)s/p
/div # variables: (title, parameters, description)HEADING_ATTRIBUTE_TMPL p classattributestrong%(name)s:/strong %(value)s/p# variables: (name, value)# ------------------------------------------------------------------------# Report#REPORT_TMPL
p idshow_detail_lineShow
a hrefjavascript:showCase(0)Summary/a
a hrefjavascript:showCase(1)Failed/a
a hrefjavascript:showCase(2)All/a
/p
table idresult_table
colgroup
col alignleft /
col alignright /
col alignright /
col alignright /
col alignright /
col alignright /
/colgroup
tr idheader_rowtdTest Group/Test case/tdtdCount/tdtdPass/tdtdFail/tdtdError/tdtdView/td
/tr
%(test_list)s
tr idtotal_rowtdTotal/tdtd%(count)s/tdtd%(Pass)s/tdtd%(fail)s/tdtd%(error)s/tdtdnbsp;/td
/tr
/table# variables: (test_list, count, Pass, fail, error)REPORT_CLASS_TMPL r
tr class%(style)std%(desc)s/tdtd%(count)s/tdtd%(Pass)s/tdtd%(fail)s/tdtd%(error)s/tdtda hrefjavascript:showClassDetail(%(cid)s,%(count)s)Detail/a/td
/tr# variables: (style, desc, count, Pass, fail, error, cid)REPORT_TEST_WITH_OUTPUT_TMPL r
tr id%(tid)s class%(Class)std class%(style)sdiv classtestcase%(desc)s/div/tdtd colspan5 aligncenter!--css div popup start--a classpopup_link οnfοcusthis.blur(); hrefjavascript:showTestDetail(div_%(tid)s) %(status)s/adiv iddiv_%(tid)s classpopup_windowdiv styletext-align: right; color:red;cursor:pointera οnfοcusthis.blur(); οnclickdocument.getElementById(div_%(tid)s).style.display none [x]/a/divpre%(script)s/pre/div!--css div popup end--/td
/tr# variables: (tid, Class, style, desc, status)REPORT_TEST_NO_OUTPUT_TMPL r
tr id%(tid)s class%(Class)std class%(style)sdiv classtestcase%(desc)s/div/tdtd colspan5 aligncenter%(status)s/td
/tr# variables: (tid, Class, style, desc, status)REPORT_TEST_OUTPUT_TMPL r
%(id)s: %(output)s# variables: (id, output)# ------------------------------------------------------------------------# ENDING#ENDING_TMPL div idendingnbsp;/div# -------------------- The end of the Template class -------------------TestResult unittest.TestResultclass _TestResult(TestResult):# note: _TestResult is a pure representation of results.# It lacks the output and reporting ability compares to unittest._TextTestResult.def __init__(self, verbosity1):TestResult.__init__(self)self.stdout0 Noneself.stderr0 Noneself.success_count 0self.failure_count 0self.error_count 0self.verbosity verbosity# result is a list of result in 4 tuple# (# result code (0: success; 1: fail; 2: error),# TestCase object,# Test output (byte string),# stack trace,# )self.result []def startTest(self, test):TestResult.startTest(self, test)# just one buffer for both stdout and stderrself.outputBuffer io.StringIO()stdout_redirector.fp self.outputBufferstderr_redirector.fp self.outputBufferself.stdout0 sys.stdoutself.stderr0 sys.stderrsys.stdout stdout_redirectorsys.stderr stderr_redirectordef complete_output(self):Disconnect output redirection and return buffer.Safe to call multiple times.if self.stdout0:sys.stdout self.stdout0sys.stderr self.stderr0self.stdout0 Noneself.stderr0 Nonereturn self.outputBuffer.getvalue()def stopTest(self, test):# Usually one of addSuccess, addError or addFailure would have been called.# But there are some path in unittest that would bypass this.# We must disconnect stdout in stopTest(), which is guaranteed to be called.self.complete_output()def addSuccess(self, test):self.success_count 1TestResult.addSuccess(self, test)output self.complete_output()self.result.append((0, test, output, ))if self.verbosity 1:sys.stderr.write(ok )sys.stderr.write(str(test))sys.stderr.write(\n)else:sys.stderr.write(.)def addError(self, test, err):self.error_count 1TestResult.addError(self, test, err)_, _exc_str self.errors[-1]output self.complete_output()self.result.append((2, test, output, _exc_str))if self.verbosity 1:sys.stderr.write(E )sys.stderr.write(str(test))sys.stderr.write(\n)else:sys.stderr.write(E)def addFailure(self, test, err):self.failure_count 1TestResult.addFailure(self, test, err)_, _exc_str self.failures[-1]output self.complete_output()self.result.append((1, test, output, _exc_str))if self.verbosity 1:sys.stderr.write(F )sys.stderr.write(str(test))sys.stderr.write(\n)else:sys.stderr.write(F)class HTMLTestRunner(Template_mixin):def __init__(self, streamsys.stdout, verbosity1, titleNone, descriptionNone):self.stream streamself.verbosity verbosityif title is None:self.title self.DEFAULT_TITLEelse:self.title titleif description is None:self.description self.DEFAULT_DESCRIPTIONelse:self.description descriptionself.startTime datetime.datetime.now()def run(self, test):Run the given test case or test suite.result _TestResult(self.verbosity)test(result)self.stopTime datetime.datetime.now()self.generateReport(test, result)print(sys.stderr, Time Elapsed: %s % (self.stopTime-self.startTime))return resultdef sortResult(self, result_list):# unittest does not seems to run in any particular order.# Here at least we want to group them together by class.rmap {}classes []for n,t,o,e in result_list:cls t.__class__if not cls in rmap:rmap[cls] []classes.append(cls)rmap[cls].append((n,t,o,e))r [(cls, rmap[cls]) for cls in classes]return rdef getReportAttributes(self, result):Return report attributes as a list of (name, value).Override this to add custom attributes.startTime str(self.startTime)[:19]duration str(self.stopTime - self.startTime)status []if result.success_count: status.append(Pass %s % result.success_count)if result.failure_count: status.append(Failure %s % result.failure_count)if result.error_count: status.append(Error %s % result.error_count )if status:status .join(status)else:status nonereturn [(Start Time, startTime),(Duration, duration),(Status, status),]def generateReport(self, test, result):report_attrs self.getReportAttributes(result)generator HTMLTestRunner %s % __version__stylesheet self._generate_stylesheet()heading self._generate_heading(report_attrs)report self._generate_report(result)ending self._generate_ending()output self.HTML_TMPL % dict(title saxutils.escape(self.title),generator generator,stylesheet stylesheet,heading heading,report report,ending ending,)self.stream.write(output.encode(utf8))def _generate_stylesheet(self):return self.STYLESHEET_TMPLdef _generate_heading(self, report_attrs):a_lines []for name, value in report_attrs:line self.HEADING_ATTRIBUTE_TMPL % dict(name saxutils.escape(name),value saxutils.escape(value),)a_lines.append(line)heading self.HEADING_TMPL % dict(title saxutils.escape(self.title),parameters .join(a_lines),description saxutils.escape(self.description),)return headingdef _generate_report(self, result):rows []sortedResult self.sortResult(result.result)for cid, (cls, cls_results) in enumerate(sortedResult):# subtotal for a classnp nf ne 0for n,t,o,e in cls_results:if n 0: np 1elif n 1: nf 1else: ne 1# format class descriptionif cls.__module__ __main__:name cls.__name__else:name %s.%s % (cls.__module__, cls.__name__)doc cls.__doc__ and cls.__doc__.split(\n)[0] or desc doc and %s: %s % (name, doc) or namerow self.REPORT_CLASS_TMPL % dict(style ne 0 and errorClass or nf 0 and failClass or passClass,desc desc,count npnfne,Pass np,fail nf,error ne,cid c%s % (cid1),)rows.append(row)for tid, (n,t,o,e) in enumerate(cls_results):self._generate_report_test(rows, cid, tid, n, t, o, e)report self.REPORT_TMPL % dict(test_list .join(rows),count str(result.success_countresult.failure_countresult.error_count),Pass str(result.success_count),fail str(result.failure_count),error str(result.error_count),)return reportdef _generate_report_test(self, rows, cid, tid, n, t, o, e):# e.g. pt1.1, ft1.1, etchas_output bool(o or e)tid (n 0 and p or f) t%s.%s % (cid1,tid1)name t.id().split(.)[-1]doc t.shortDescription() or desc doc and (%s: %s % (name, doc)) or nametmpl has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL# o and e should be byte string because they are collected from stdout and stderr?if isinstance(o,str):# TODO: some problem with string_escape: it escape \n and mess up formating# uo unicode(o.encode(string_escape))uo eelse:uo oif isinstance(e,str):# TODO: some problem with string_escape: it escape \n and mess up formating# ue unicode(e.encode(string_escape))ue eelse:ue escript self.REPORT_TEST_OUTPUT_TMPL % dict(id tid,output saxutils.escape(uoue),)row tmpl % dict(tid tid,Class (n 0 and hiddenRow or none),style n 2 and errorCase or (n 1 and failCase or none),desc desc,script script,status self.STATUS[n],)rows.append(row)if not has_output:returndef _generate_ending(self):return self.ENDING_TMPL##############################################################################
# Facilities for running tests from the command line
############################################################################### Note: Reuse unittest.TestProgram to launch test. In the future we may
# build our own launcher to support more specific command line
# parameters like test title, CSS, etc.
class TestProgram(unittest.TestProgram):A variation of the unittest.TestProgram. Please refer to the baseclass for command line parameters.def runTests(self):# Pick HTMLTestRunner as the default test runner.# base classs testRunner parameter is not useful because it means# we have to instantiate HTMLTestRunner before we know self.verbosity.if self.testRunner is None:self.testRunner HTMLTestRunner(verbosityself.verbosity)unittest.TestProgram.runTests(self)main TestProgram##############################################################################
# Executing this module from the command line
##############################################################################if __name__ __main__:main(moduleNone)
2️⃣运行测试用例并生成html文件
HTMLTestRunner主要有三个参数
stream测试报告写入文件的存储位置。title测试报告的标题。description测试报告的描述。
import unittest
from HTMLTestRunner import HTMLTestRunner
# 自动发现当前目录下的所有测试模块并运行
suite unittest.TestSuite()
case unittest.defaultTestLoader.discover(start_dir.,patterntest*.py)suite.addTest(case)# 创建测试运行器执行测试套件是固定的使用方法
with open(测试报告.html,wb) as f:runner HTMLTestRunner(streamf,title测试报告,description这是项目的描述。。。)runner.run(suite) 九、unittest测试框架的设计与实现
1、对项目重构
新建文件夹reports专门存放测试报告新建文件夹data专门存放测试数据新建文件夹case专门存放测试用例新建文件夹utils专门第三方的包等工具把utils中的HTMLTestRunner.py变成一个包在utils文件夹中新建一个__init__.py 的空文件。这个文件告诉 Python 该目录应该被视为一个包。 修改代码中start_dir的值.为case即case unittest.defaultTestLoader.discover(start_dircase,patterntest*.py) 修改代码中测试报告的路径为with open(reports/测试报告.html, wb) as f: 改造测试报告的名字以便可以保存历史报告首先引入time模块获取现在的时间年-月-日-时-分-秒设置reportpath变量替换测试报告的存放路径。 重构后的文件结构 修改后的main.py文件
import unittest
import time
from utils.HTMLTestRunner import HTMLTestRunner
# 自动发现当前目录下的所有测试模块并运行
suite unittest.TestSuite()
case unittest.defaultTestLoader.discover(start_dircase,patterntest*.py)suite.addTest(case)now time.strftime(%Y-%m-%d-%H-%M-%S)
reportPath freports/测试报告-{now}.html# 创建测试运行器执行测试套件是固定的使用方法
with open(reportPath, wb) as f:runner HTMLTestRunner(streamf,title测试报告,description这是项目的描述。。。)runner.run(suite) 2、处理excel表的测试数据
下载第三方的包openpyxl
pip install openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple
在utils中封装一个excelFunc.py的包专门用来读excel表的数据
from openpyxl import load_workbookdef readExcleData(fileName,sheetName,coor):读取excle的数据excle load_workbook(fileName)sheet excle[sheetName]min_row coor.get(min_row)max_row coor.get(max_row)min_col coor.get(min_col)max_col coor.get(max_col)dataList sheet.iter_rows(min_row,max_row,min_col,max_col)result []for row in dataList:rows []for i in row:rows.append(i.value)result.append(rows)return resultif __name__ __main__:coor {min_row:1, # 根据需求修改数字max_row:1,min_col:1,max_col:1}data readExcleData(../data/测试数据.xlsx,Sheet1,coor)print(data)
如果excel表中是 读取2到4行1到4列的结果 这样在做数据驱动测试的时候就可以使用ddt来处理测试数据了
十、使用邮件接收测试结果
准备工作在邮箱的设置中开启amtp功能然后生成一个授权码 以qq邮箱为例子 在utils中封装一个emailFunc.py的包实现发送邮件的功能
import smtplib
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipartdef sendEmail(receiver,filename):sender xxxxxxxqq.com # 改成发送人的具体邮箱必须是有效的不然会发送失败passwd abcdxxxxxxx # 不是邮箱密码而是授权码content MIMEMultipart()content[From] sendercontent[To] receivercontent[Subject] Python SMTP 邮件测试# 添加文本内容text MIMEText(测试报告已出详情见附件, plain, utf-8)content.attach(text)# 添加附件with open(filename, rb) as f:attachment MIMEApplication(f.read()) # 读取为附件attachment.add_header(Content-Disposition, attachment, filename测试报告.html)content.attach(attachment)try:# 建立 SMTP 、SSL 的连接连接发送方的邮箱服务器smtp smtplib.SMTP_SSL(smtp.qq.com, 465)# 登录发送方的邮箱账号smtp.login(sender, passwd)# 发送邮件 发送方接收方发送的内容smtp.sendmail(sender, receiver, content.as_string())print(邮件发送成功)smtp.quit()except Exception as e:print(e)print(发送邮件失败)
增加main.py的内容添加了emailFunc包以及最后一行调用endEmail代码
import unittest
import time
from utils.HTMLTestRunner import HTMLTestRunner
from utils.emailFunc import sendEmail
# 自动发现当前目录下的所有测试模块并运行
suite unittest.TestSuite()
case unittest.defaultTestLoader.discover(start_dircase,patterntest*.py)suite.addTest(case)now time.strftime(%Y-%m-%d-%H-%M-%S)
reportPath freports/测试报告-{now}.html# 创建测试运行器执行测试套件是固定的使用方法
with open(reportPath, wb) as f:runner HTMLTestRunner(streamf,title测试报告,description这是项目的描述。。。)runner.run(suite)# 测试报告以邮件发送给xxxxxxxqq.com
sendEmail(xxxxxxxqq.com,reportPath)代码可参考python 学习笔记5——SMTP 使用QQ邮箱发送邮件_qq邮箱smtp-CSDN博客 全部内容Unittest01|TestCase、断言、装饰器、夹具、清理函数、ddt-CSDN博客Unittest02|TestSuite、TestRunner、HTMLTestRunner、处理excel表数据、邮件接收测试结果-CSDN博客