Time will tell.
一、执行模块介绍
分两个大类:TextTestResult、TextTestRunner。其中 TextTestRunner 是执行的主要模块。
执行用例就需要写结果,因此这两块功能是一体的,在执行类的开始就声明了结果类,如果没有传入结果类,那么就使用默认的 TextTestResult 来处理结果。
代码:
def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1,
failfast=False, buffer=False, resultclass=None):
self.stream = _WritelnDecorator(stream)
self.descriptions = descriptions
self.verbosity = verbosity
self.failfast = failfast
self.buffer = buffer
if resultclass is not None:
self.resultclass = resultclass
这里可以看到默认是按照标准输出来输出的。
init 方法之后,就是 run 方法了。
def run(self, test):
"Run the given test case or test suite."
result = self._makeResult()
registerResult(result)
result.failfast = self.failfast
result.buffer = self.buffer
startTime = time.time()
startTestRun = getattr(result, 'startTestRun', None)
if startTestRun is not None:
startTestRun()
try:
test(result)
finally:
stopTestRun = getattr(result, 'stopTestRun', None)
if stopTestRun is not None:
stopTestRun()
stopTime = time.time()
timeTaken = stopTime - startTime
result.printErrors()
可以看到在调用执行之前,做了一些结果类的注册和处理。再往下都是一些结果处理的逻辑,比如发现有跳过的用例,要把这部分信息打到 stream 里面,类似这样的逻辑由一堆。
二、HTMLTestRunner
HTMLTestRunner.py
是一个 unittest 测试报告的输出类,这个是第三方编写的,我们可以通过这个方法,来看看执行类是这么调用的。
HTMLTestRunner.py
生成的报告是一个 html 格式的报告,从代码和介绍中都能看的出来,生成 html 的方式其实是通过硬编码 html 的模板,然后在这个模板填充执行结果的数据。
主类HTMLTestRunner
继承了Template_mixin
,Template_mixin
实际上就是我们说的硬编码的模板。
我们在调用的时候,代码通常是这样的:
filename = os.getcwd() + "/xxx_{tm}.html".format(
tm=time.strftime("%Y-%m-%d %H%M%S"))
fp = file(filename, "wb")
runner = HTMLTestRunner.HTMLTestRunner(
stream=fp,
title="xxx_{tm}.html".format(tm=time.strftime("%Y-%m-%d %H%M%S")),
description=u'本次测试的案例数为:' + str(x) + u'条'
)
runner.run(testunit)
按照实际定义文件名,确保文件名不重复,接着定义一个文件句柄。然后初始化主类HTMLTestRunner
,把文件句柄,标题和描述传进去,最后调用run
方法。
def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
self.stream = stream
self.verbosity = verbosity
if title is None:
self.title = self.DEFAULT_TITLE
else:
self.title = title
if description is None:
self.description = self.DEFAULT_DESCRIPTION
else:
self.description = description
self.startTime = datetime.datetime.now()
通过代码可以看到,传入的文件句柄,就是 stream ,默认是标准输出,传入文件句柄后,则会把内容输出到文件中。详细程度默认是1。
接着是执行方法。
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, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
return result
我们这边传入的 test 实际上是testSuite
。这里的run
方法一开始注册的结果类_TestResult
集成自TestResult
。
可以看到,重写的方法中:
def startTest(self, test):
TestResult.startTest(self, test)
# just one buffer for both stdout and stderr
self.outputBuffer = StringIO.StringIO()
stdout_redirector.fp = self.outputBuffer
stderr_redirector.fp = self.outputBuffer
self.stdout0 = sys.stdout
self.stderr0 = sys.stderr
sys.stdout = stdout_redirector
sys.stderr = stderr_redirector
重新定义了句柄。把标准输出的内容替换成输出到的文件的句柄
class OutputRedirector(object):
""" Wrapper to redirect stdout or stderr """
def __init__(self, fp):
self.fp = fp
def 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)
而句柄的声明,实际上是这个文件。
句柄处理完之后,开始执行用例,执行完毕之后,调用generateReport来吧结果写入文件。
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'))
这里实际上就是把之前硬编码的 html 做一个组装,然后调用 stream ,也就是我们传入的文件句柄进行数据写入。
实际上 HTMLTestRunner 也是支持命令行的方式,不过命令行的启动方式目前只支持原生的启动方式。
class TestProgram(unittest.TestProgram):
"""
A variation of the unittest.TestProgram. Please refer to the base
class for command line parameters.
"""
def runTests(self):
# Pick HTMLTestRunner as the default test runner.
# base class's 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(verbosity=self.verbosity)
unittest.TestProgram.runTests(self)
main = TestProgram
注释声明了,目前还没有实现个性化的启动方式。
.
好了,以上就分享到这里,如果你对更多内容、及Python实例练习题、面试题、自动化软件测试感兴趣的话可以加入我们175317069一起学习喔。群里会有各项学习资源发放,更有行业深潜多年的技术人分析讲解。期待你的加入!
最后祝愿你能成为一名优秀的软件测试工程师!
欢迎【评论】、【点赞】、【关注】~
Time will tell.(时间会证明一切)