python3 unittest模块源码解析(二) --- 执行器runner

目录

 

一、runner简介

二、TextTestRunner源码解析

1. 类的实例化

2. run方法

三、总结


一、runner简介

官方文档的说明是:

A basic test runner implementation that outputs results to a stream.

一个基本的runner组件是用来将结果输出到数据流。

 根据它的解释,runner的最终作用是得到一个result对象,即结果,并将其反馈给用户。在上一章中,我们了解到runner的本质是实例化的TextTestRunner类,我们最终调用该实例的run(test)方法执行测试并得到结果,本章我们来分析该类的源码来了解这一过程如何实现。


二、TextTestRunner源码解析

TextTestRunner的位置在runner.py文件中

1. 类的实例化

展示__init__方法的源码:

class TextTestRunner(object):
    resultclass = TextTestResult    # 为resultclass属性赋初始值

    def __init__(self, stream=None, descriptions=True, verbosity=1,
                 failfast=False, buffer=False, resultclass=None, warnings=None,
                 *, tb_locals=False):
        if stream is None:
            stream = sys.stderr
        self.stream = _WritelnDecorator(stream)
        self.descriptions = descriptions
        self.verbosity = verbosity
        self.failfast = failfast
        self.buffer = buffer
        self.tb_locals = tb_locals
        self.warnings = warnings
        if resultclass is not None:    # 如果实例化时指定了该参数,则覆盖它的初始值
            self.resultclass = resultclass

都是简单的属性赋值,各属性含义可查看笔者手译的unittest.TextTestRunner部分。这里需要注意self.resultclass属性,代码中在__init__()方法之外,为该属性赋了一个默认值。如果用户在实例化时的参数中给定了该参数,则会覆盖这个值。

 

2. run方法

源码:

    def _makeResult(self):
        # 创建result对象
        return self.resultclass(self.stream, self.descriptions, self.verbosity)

    def run(self, test):
        "Run the given test case or test suite."

        result = self._makeResult()            # 创建result对象
        registerResult(result)                 # 为control-c操作绑定这个result对象
        result.failfast = self.failfast        # 是否遇到失败或错误时就停止测试
        result.buffer = self.buffer
        result.tb_locals = self.tb_locals
        with warnings.catch_warnings():
            if self.warnings:
                # warning过滤器
                warnings.simplefilter(self.warnings)
                if self.warnings in ['default', 'always']:
                    warnings.filterwarnings('module',
                            category=DeprecationWarning,
                            message=r'Please use assert\w+ instead.')
            startTime = time.time()
            startTestRun = getattr(result, 'startTestRun', None)
            if startTestRun is not None:
                startTestRun()                 # test运行之前被调用的方法
            try:
                test(result)                   # 这里的test是TestSuite类实例,调用它时等用于调用它的run方法
            finally:
                stopTestRun = getattr(result, 'stopTestRun', None)
                if stopTestRun is not None:
                    stopTestRun()              # test完全运行完之后被调用的方法
            stopTime = time.time()
        timeTaken = stopTime - startTime       # 计算测试耗时
        result.printErrors()
        if hasattr(result, 'separator2'):
            self.stream.writeln(result.separator2)
        run = result.testsRun                  # 测试用例的数量
        self.stream.writeln("Ran %d test%s in %.3fs" %
                            (run, run != 1 and "s" or "", timeTaken))        # 打印运行的测试总数
        self.stream.writeln()        #打印空行

        expectedFails = unexpectedSuccesses = skipped = 0
        try:
            results = map(len, (result.expectedFailures,
                                result.unexpectedSuccesses,
                                result.skipped))                # 统计括号中3种情况的次数
        except AttributeError:
            pass
        else:
            expectedFails, unexpectedSuccesses, skipped = results

        infos = []
        if not result.wasSuccessful():
            # 如果测试存在失败或错误,在结果信息中添加错误或失败的数量
            # 如果测试成功,显示"OK"
            self.stream.write("FAILED")
            failed, errored = len(result.failures), len(result.errors)
            if failed:
                infos.append("failures=%d" % failed)
            if errored:
                infos.append("errors=%d" % errored)
        else:
            self.stream.write("OK")
        
        # 统计跳过、预期的失败及非预期的成功的数量
        if skipped:
            infos.append("skipped=%d" % skipped)
        if expectedFails:
            infos.append("expected failures=%d" % expectedFails)
        if unexpectedSuccesses:
            infos.append("unexpected successes=%d" % unexpectedSuccesses)
        if infos:
            self.stream.writeln(" (%s)" % (", ".join(infos),))
        else:
            self.stream.write("\n")
        return result

在run方法中,先用self._makeResult方法创建result对象。在with warning.catch_warnings代码块中执行测试可以测试用例执行过程中的警告进行过滤(因为unittest中有很多废弃的曾用名,可能会引发警告,也可以自定义过滤规则,详情可查看官方文档warning模块)。执行测试的关键代码是test(result),这里的test是一个TestSuite类的实例对象,这里以函数的方式调用等同于执行test.run(result)

剩余代码是用于打印信息,就只在源码中作标注。


三、总结

runner.py模块的内容相当简短,测试的底层实现并没有展现在该模块中,但是result对象是在它这里创建的,之后测试结果都将存放于该对象中,该类的实例是用于存放并打印结果。这里子类中重写的各中方法的作用是为了在发生相应的情况(例如:error、failure、skip等)时打印相关信息,如下图中的标注处。

下期我们会具体介绍result对象,因为他是执行test.run(result)方法时必须要用到的参数。

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值