python unittest剖析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhanghs11/article/details/80158889
  1. 先从demo开始
#!/usr/bin/env python
#-*- coding = utf-8 -*-

import os
import sys
import unittest

class MydemoA(unittest.TestCase):
    def setUp(self):
        print("MydemoA setUp ... ")
        self.a = 1
    def test_demo_a1(self):
        self.a += 1
        print("I am test_demo_a1 the value of a is {}".format(self.a))
    def test_demo_a2(self):
        self.a += 1
        print("I am test_demo_a2 the value of a is {}".format(self.a))
    def test_demo_a3(self):
        self.a += 1
        print("I am test_demo_a3 the value of a is {}".format(self.a))
    def tearDown(self):
        print("MydemoA tearDown ...")    


class MydemoB(unittest.TestCase):
    def setUp(self):
        print("MydemoB setUp ... ")
        self.b = 1
    def test_demo_b1(self):
        self.b += 1
        print("I am test_demo_b1 the value of b is {}".format(self.b))
    def test_demo_b2(self):
        self.b += 1
        print("I am test_demo_b2 the value of b is {}".format(self.b))
    def test_demo_b3(self):
        self.b += 1
        print("I am test_demo_b3 the value of b is {}".format(self.b))
    def tearDown(self):
        print("MydemoB tearDown ...")    

if __name__ == "__main__":
    print("开始...")
    unittest.main()
    sys.exit(0)

运行结果:

开始...
MydemoA setUp ... 
I am test_demo_a1 the value of a is 2
MydemoA tearDown ...
..MydemoA setUp ... 
I am test_demo_a2 the value of a is 2
MydemoA tearDown ...
MydemoA setUp ... 
I am test_demo_a3 the value of a is 2
MydemoA tearDown ...
.MydemoB setUp ... 
I am test_demo_b1 the value of b is 2
MydemoB tearDown ...
.MydemoB setUp ... 
I am test_demo_b2 the value of b is 2
MydemoB tearDown ...
.MydemoB setUp ... 
I am test_demo_b3 the value of b is 2
MydemoB tearDown ...

看来光看代码不行,需要调试,和理解的还是不一样的。setUp和tearDown的每个testcase的都要执行。

unittest剖析

__all__ = ['TestResult', 'TestCase', 'TestSuite',
           'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main',
           'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless',
           'expectedFailure', 'TextTestResult', 'installHandler',
           'registerResult', 'removeResult', 'removeHandler']

# Expose obsolete functions for backwards compatibility
__all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])

__unittest = True

from .result import TestResult
from .case import (TestCase, FunctionTestCase, SkipTest, skip, skipIf,skipUnless, expectedFailure)
from .suite import BaseTestSuite, TestSuite
from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames,findTestCases)
from .main import TestProgram, main
from .runner import TextTestRunner, TextTestResult
from .signals import installHandler, registerResult, removeResult, removeHandler

unittest如源码有多个模块,测试的话一般要重点关注TestCase和TestResult。至于TestSuite和TextTestRunner等等,平时也会用到,但其最终执行的是TestCase中的run方法,并把结果给TestResult(或它的子类)。

unittest.main()最终执行TestProgram类的init()方法

def __init__(self, module='__main__', defaultTest=None, argv=None,testRunner=None, testLoader=loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None,buffer=None, warnings=None):
    if isinstance(module, str):
        self.module = __import__(module)
        for part in module.split('.')[1:]:
            self.module = getattr(self.module, part)
   else:
        self.module = module
   if argv is None:
        argv = sys.argv   #得到当前模块的绝对路径

    self.exit = exit
    self.failfast = failfast
    self.catchbreak = catchbreak
    self.verbosity = verbosity
    self.buffer = buffer
    if warnings is None and not sys.warnoptions:
        self.warnings = 'default'
    else:
        self.warnings = warnings
    self.defaultTest = defaultTest
    self.testRunner = testRunner
    self.testLoader = testLoader
    self.progName = os.path.basename(argv[0])
    self.parseArgs(argv)  #查找当前module的Testsuite
    self.runTests()   #执行测试

主要的步骤就是第一:找出要测试的testcase,并加入到Testsuite,第二:运行Testsuite并把结果给TestResult。

TesetCase就是以test开头的就叫一个testcase,我只能这样说太偏面的,准确的说:是实例了一个TesetCase类的叫一个TestCase,比如这样:

#!/usr/bin/env python
#-*- coding = utf-8 -*-

import os
import sys
import unittest

class MydemoA(unittest.TestCase):
    def setUp(self):
        print("MydemoA setUp ... ")
        self.a = 1
    def test_demo_a1(self):
        self.a += 1
        print("I am test_demo_a1 the value of a is {}".format(self.a))
    def test_demo_a2(self):
        self.a += 1
        print("I am test_demo_a2 the value of a is {}".format(self.a))
    def test_demo_a3(self):
        self.a += 1
        print("I am test_demo_a3 the value of a is {}".format(self.a))
    def tearDown(self):
        print("MydemoA tearDown ...")    


class MydemoB(unittest.TestCase):
    def setUp(self):
        print("MydemoB setUp ... ")
        self.b = 1
    def test_demo_b1(self):
        self.b += 1
        print("I am test_demo_b1 the value of b is {}".format(self.b))
    def test_demo_b2(self):
        self.b += 1
        print("I am test_demo_b2 the value of b is {}".format(self.b))
    def test_demo_b3(self):
        self.b += 1
        print("I am test_demo_b3 the value of b is {}".format(self.b))
    def tearDown(self):
        print("MydemoB tearDown ...")    

if __name__ == "__main__":
    print("开始...")
    test_runner = unittest.TextTestRunner()
    test_suit = unittest.TestSuite()
    test_suit.addTests(map(MydemoA,["test_demo_a1","test_demo_a2"]))
    test_suit.addTests(map(MydemoB,["test_demo_b1","test_demo_b3"]))
    test_runner.run(test_suit)
    sys.exit(0)

运行结果:

.开始...
..MydemoA setUp ... 
I am test_demo_a1 the value of a is 2
MydemoA tearDown ...
MydemoA setUp ... 
I am test_demo_a2 the value of a is 2
MydemoA tearDown ...
MydemoB setUp ... 
I am test_demo_b1 the value of b is 2
MydemoB tearDown ...
MydemoB setUp ... 
I am test_demo_b3 the value of b is 2
MydemoB tearDown ...
.

执行了addTest的方法

loader.py模块有defaultTestLoader = TestLoader(),TestLoader这个类第一行就看见testMethodPrefix = ‘test’,也就是说如果你使用到defaultTestLoader,那么默认是以test开头的方法为一个用例,具体可以在TestLoader类中的getTestCaseNames得到实现,红字注释部分为什么testCaseClass要有call方法,我们后面提到。

    def getTestCaseNames(self, testCaseClass):
        """Return a sorted sequence of method names found within testCaseClass
        """
        def isTestMethod(attrname, testCaseClass=testCaseClass,
                         prefix=self.testMethodPrefix):
            return attrname.startswith(prefix) and \
                callable(getattr(testCaseClass, attrname))
                # 返回一个testCaseClass有__call__方法且attrname以prefix开头的为一个testcase
        testFnNames = list(filter(isTestMethod, dir(testCaseClass)))
        if self.sortTestMethodsUsing:
            testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing))
        return testFnNames

上文提到的unittest.main()其实用的就是defaultTestLoader,执行test开头的方法。
unittest.main() 和 test_runner.run(test_suit)两种方法。

这个类里面包含了我们所能用的方法。我列出来一些主要的吧。

setUp()在每个test执行前都要执行的方法。

tearDown()在每个test执行后都要执行的方法。(不管是否执行成功)

setUpClass()在一个测试类中在所有test开始之前,执行一次且必须使用到Testsuite(只有在TestSuite的run方法里面才对其调用)

tearDownClass()在一个测试类中在所有test结束之后,执行一次且必须使用到Testsuite(只有在TestSuite的run方法里面才对其调用)

run()这是unnitest的核心,逻辑也相对复杂,但是很好理解,具体自己看源码。所有最终case的执行都会归结到该run方法。

还有一个重要的_resultForDoCleanups私有变量,存储TestResult的执行结果,这个在构建后面的skip用到。

https://www.cnblogs.com/hhudaqiang/p/6596043.html

猜你喜欢

转载自blog.csdn.net/zhanghs11/article/details/80158889