当前位置:网站首页>unittest 如何知道每个测试用例的执行时间

unittest 如何知道每个测试用例的执行时间

2022-06-11 16:21:00 qq_37668510

unittest在执行完所有用例时,控制台会输出所有用例的执行的时间。
用例执行结果
具体是如何实现的,直接看源码:unittest.TextTestRunner类中的run方法。

class TextTestRunner(object):
	# ....省略部分代码
	
    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
        result.tb_locals = self.tb_locals
        with warnings.catch_warnings():
            if self.warnings:
                warnings.simplefilter(self.warnings)
                if self.warnings in ['default', 'always']:
                    warnings.filterwarnings('module',
                            category=DeprecationWarning,
                            message=r'Please use assert\w+ instead.')  
            # 获取时间主要代码:获取开始时间
            startTime = time.perf_counter()
            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.perf_counter()
        # 运行测试用例的总时间
        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()

       # ....省略部分代码
        return result

源码中只提供了获取用例总的运行时间,那么如何才能获取每个用例的时间?

思路

我想到了几种方式可以获取:

  • 第一种:使用setUptearDown 钩子函数实现(不推荐)。
  • 第二种:使用装饰器(不推荐)。
  • 第三种:重新TextTestResult类(推荐)。

1. 使用钩子函数实现(不推荐)

缺点:每一个测试类都需要写重复的代码,所以不推荐。

import unittest
import time

class TestDemo(unittest.TestCase):

    def setUp(self):
        self.startTime = time.perf_counter()
       

    def test_01(self):
        time.sleep(1)
        print('this is testcase one')

    def test_02(self):
        print('this is testcase two')

    def tearDown(self):
        self.timeTaken = time.perf_counter() - self.startTime
        print('当前用例执行时间:{:.10f}s'.format(self.timeTaken))
        

if __name__ == "__main__":
    unittest.main()

结果:

this is testcase one
当前用例执行时间:1.0053873200s
.this is testcase two
当前用例执行时间:0.0000630220s
.
----------------------------------------------------------------------
Ran 2 tests in 1.006s

2. 使用装饰器方式实现(不推荐)

使用装饰器,看似比钩子函数解决了写很多重复代码的问题,但是还不够完美,不推荐。

from re import L
import re
import unittest
import time
import functools


def CustomTimes(func):
    @functools.wraps(func)
    def wrapps(self):
        start_time = time.perf_counter()
        func(self) # 执行测试用例
        timeTaken = time.perf_counter() - start_time
        print('当前用例执行时间:{:.10f}s'.format(timeTaken))
    return wrapps



class TestDemo(unittest.TestCase):

    @CustomTimes
    def test_01(self):
        time.sleep(1)
        print('this is testcase one')

    @CustomTimes
    def test_02(self):
        print('this is testcase two')
        

if __name__ == "__main__":
    unittest.main()

结果:

this is testcase one
当前用例执行时间:1.0051255180s
.this is testcase two
当前用例执行时间:0.0000147200s
.
----------------------------------------------------------------------
Ran 2 tests in 1.006s

3. 重新TextTestResult类(推荐)

TextTestResult的继承关系:TextTestResult–>继承–>TestResult,在TestResult 中,有两个方法需要重写,这些方法都会在用例执行是自动运行

TestResult部分源码:

这是一个我们需要重新的方法,需要记录测试用例开始执行的时间。


    def startTest(self, test):
        "Called when the given test is about to be run"
        self.testsRun += 1
        self._mirrorOutput = False
        self._setupStdout()

还有一个是用例执行成功的方法addSuccess,用例执行成功时会调用这个方法,所以我准备用例记录用例的结束时间。

    def addSuccess(self, test):
        "测试成功完成时调用"
        pass

代码:

import unittest
import time
from unittest import TextTestResult as _TestTestResult, TextTestRunner as _TextTestRunner

class CTextTestResult(_TestTestResult):
    def __init__(self,*args, **kwargs):
        super(CTextTestResult,self).__init__(*args, **kwargs)
        self.times = [] # 用于记录测试时间
    
    def startTest(self, test: unittest.case.TestCase) -> None:
        # 测试开始执行之前调用
        self.start_time = time.time() 
        super(CTextTestResult,self).startTest(test)


    def addSuccess(self, test: unittest.case.TestCase) -> None:
    	# 测试完成时调用
        total_time = time.time() - self.start_time
        test_name = self.getDescription(test)
        self.times.append((test_name,total_time))
        super(CTextTestResult,self).addSuccess(test)
        
    def getTestTimeing(self):
       # 返回记录的所有用例的时间
        return self.times

super(CTextTestResult,self).startTest(test)
super(CTextTestResult,self).addSuccess(test)

这两句非常重要,super用于调用父类的方法,在某个方法中添加新的功能,也不能把旧的功能丢了,所以之前的功能也需要保留。

最后一步:重写TextTestRunnerTextTestRunner记录了所有信息,可以获取我们自定义的属性和方法。

class CTextTestRunner(_TextTestRunner):
    def run(self, test):
        result = super(CTextTestRunner,self).run(test)
        for test_name, total_time in result.getTestTimeing():
            print("({:.10}s) {}".format(format(total_time, 'f'), test_name))
        return result

我们自定义的东西都存在result 中了。

4.完整代码

import unittest
import time
from unittest import TextTestResult as _TestTestResult, TextTestRunner as _TextTestRunner

class TestDemo(unittest.TestCase):

    def test_01(self):
        time.sleep(1)
        print('this is testcase one')

    def test_02(self):
        print('this is testcase two')

    def tearDown(self) -> None:
        return super().tearDown()

class CTextTestResult(_TestTestResult):
    def __init__(self,*args, **kwargs):
        super(CTextTestResult,self).__init__(*args, **kwargs)
        self.times = []
    
    def startTest(self, test: unittest.case.TestCase) -> None:
        self.start_time = time.time()
        super(CTextTestResult,self).startTest(test)


    def addSuccess(self, test: unittest.case.TestCase) -> None:
        total_time = time.time() - self.start_time
        test_name = self.getDescription(test)
        self.times.append((test_name,total_time))
        super(CTextTestResult,self).addSuccess(test)
        
    def getTestTimeing(self):
        return self.times

class CTextTestRunner(_TextTestRunner):
    def run(self, test):
        result = super(CTextTestRunner,self).run(test)
        for test_name, total_time in result.getTestTimeing():
            print("({:.10}s) {}".format(format(total_time, 'f'), test_name))
        return result


if __name__ == "__main__":
    test_load = unittest.defaultTestLoader
    case = test_load.loadTestsFromTestCase(TestDemo)

    runner = CTextTestRunner(resultclass=CTextTestResult)
    result = runner.run(case)
    

原网站

版权声明
本文为[qq_37668510]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_37668510/article/details/125169500