unittest框架+HTMLTestRunner生成报告教程

unittest测试框架学习
python官网unittest框架的介绍 https://docs.python.org/3/library/unittest.html

1、unittest以面向对象的方式支持一些重要概念
  • test fixture
      test fixture表示执行一个或多个测试所需的准备工作,以及任何相关的清理操作。例如,这可能涉及创建临时或代理数据库、目录,或启动服务器进程。
    • setUp()
      这是在调用测试方法之前立即调用的;除了AssertionError或SkipTest之外,此方法引发的任何异常都将被视为错误。
    • tearDown()
      在调用测试方法并记录结果后立即调用的方法。即使测试方法引发异常,也会调用此方法,因此子类中的实现可能需要特别小心检查内部状态。除AssertionError或SkipTest外,此方法引发的任何异常都将被视为额外错误,而不是测试失败(从而增加报告的错误总数)。无论测试方法的结果如何,只有在setUp()成功的情况下才会调用此方法。默认实现不执行任何操作。
    • setUpClass()
      在运行单个类中的测试之前调用的类方法。调用setUpClass时,类是唯一的参数,并且必须装饰为classmethod(),如:
      @classmethod
      def setUpClass(cls):
        …
    • tearDownClass()
      在单个类中的测试运行后调用的类方法。tearDownClass是以类作为唯一参数调用的,并且必须装饰为classmethod()
  • test case
      test case是单独的测试单元。它检查对一组特定输入的特定响应。unittest提供了一个基类TestCase,它可以用于创建新的测试用例。
  • test suite
      test suite是测试用例、测试套件或两者的集合。它用于聚合应该一起执行的测试。
  • test runner
      test runner程序是一个组件,它协调测试的执行并向用户提供结果。跑步者可以使用图形界面、文本界面或返回特殊值来指示执行测试的结果。
2、测试用例

  可以通过继承TestCase类并且在测试类中为每一个测试添加测试方法来创建单个测试或者一组测试。每个测试方法名都需要以test_开头
  为了创建测试,我们需要使用TestCase类中的assert或者使用其中的一种assert方法。每个测试最重要的任务是调用assertEqual()来校验预期结果,调用assertTrue() 来验证条件,或者调用assertRaises() 来验证预期的异常。
  例如:

import unittest

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOo'.isupper())

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

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

直接运行结果如下:
with arguments python -m unittest unittest官网例子.TestStringMethods in D:\PythonWorkSpace\AutoTest\unit_test 表示是在 D:\PythonWorkSpace\AutoTest\unit_test 目录下执行命令 python -m unittest unittest官网例子.TestStringMethods 来执行用例的。
Ran 3 tests in 0.009s 表示运行了三个测试用例。
FAILED(failures=1) 表示有一个运行失败,并且打印出了运行失败用例的具体信息。
只有存在失败的用例,那么程序最终就会是异常退出。

"D:\Program Files\Python\Python311\python.exe" "D:/Program Files/JetBrains/PyCharm Community Edition 2023.2.1/plugins/python-ce/helpers/pycharm/_jb_unittest_runner.py" --target unittest官网例子.TestStringMethods 
Testing started at 16:25 ...
Launching unittests with arguments python -m unittest unittest官网例子.TestStringMethods in D:\PythonWorkSpace\AutoTest\unit_test
Ran 3 tests in 0.009s
FAILED (failures=1)
Failure
Traceback (most recent call last):
  File "D:\PythonWorkSpace\AutoTest\unit_test\unittest官网例子.py", line 15, in test_isupper
    self.assertTrue('FOo'.isupper())
AssertionError: False is not true
Process finished with exit code 1

也可以在命令行加上-v选项运行,会打印每个测试用例的结果,如下:
FAIL表示未通过断言校验,执行失败;
ok表示通过断言校验,执行成功。

PS D:\PythonWorkSpace\AutoTest\unit_test> python -m unittest -v unittest官网例子
test_isupper (unittest官网例子.TestStringMethods.test_isupper) ... FAIL
test_split (unittest官网例子.TestStringMethods.test_split) ... ok
test_upper (unittest官网例子.TestStringMethods.test_upper) ... ok
======================================================================
FAIL: test_isupper (unittest官网例子.TestStringMethods.test_isupper)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\PythonWorkSpace\AutoTest\unit_test\unittest官网例子.py", line 15, in test_isupper
    self.assertTrue('FOo'.isupper())
AssertionError: False is not true
----------------------------------------------------------------------
Ran 3 tests in 0.002s
FAILED (failures=1)
PS D:\PythonWorkSpace\AutoTest\unit_test>
3、setUp、tearDown、setUpClass、tearDowmClass

  上面已经介绍过,下面将演示一下setUp、tearDown和setUpClass、tearDownClass的区别。

  • setUp和tearDown
    在上面第2点中的用例中添加上setUp()和tearDown()函数,如下:
import unittest
class TestStringMethods(unittest.TestCase):
    def setUp(self):
        print("\n This is setUP!!!")
        
    def tearDown(self):
        print("\n This is tearDown!!!")
        
    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')
    def test_isupper(self):
        self.assertTrue('FOo'.isupper())
    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)
if __name__ == '__main__':
    unittest.main(verbosity = 2)

运行文件,结果如下:

PS D:\PythonWorkSpace\AutoTest\unit_test> python -m unittest -v unittest官网例子
test_isupper (unittest官网例子.TestStringMethods.test_isupper) ... 
 This is setUP!!!
FAIL
 This is tearDown!!!
test_split (unittest官网例子.TestStringMethods.test_split) ...
 This is setUP!!!
 This is tearDown!!!
ok
test_upper (unittest官网例子.TestStringMethods.test_upper) ...
 This is setUP!!!
 This is tearDown!!!
ok
======================================================================
FAIL: test_isupper (unittest官网例子.TestStringMethods.test_isupper)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\PythonWorkSpace\AutoTest\unit_test\unittest官网例子.py", line 15, in test_isupper
    self.assertTrue('FOo'.isupper())
AssertionError: False is not true
----------------------------------------------------------------------
Ran 3 tests in 0.002s
FAILED (failures=1)
PS D:\PythonWorkSpace\AutoTest\unit_test>

从打印结果中可以看出,每一个测试执行的时候,都会执行setUp()和tearDown()。

  • setUpClass和tearDownClass
    在执行一个用例的时候,只希望有一次的初始化操作和清理操作。那么可以使用setUpClass()和tearDownClass()来实现。修改上面的代码,将setUp替换成setUpClass、将tearDown替换成tearDownClass。
import unittest

class TestStringMethods(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("\n This is setUPClass!!!")

    @classmethod
    def tearDownClass(cls):
        print("\n This is tearDownClass!!!")

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')
    def test_isupper(self):
        self.assertTrue('FOo'.isupper())
    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)
if __name__ == '__main__':
    unittest.main(verbosity = 1)

运行结果如下:

PS D:\PythonWorkSpace\AutoTest\unit_test> python -m unittest -v unittest官网例子
 This is setUPClass!!!
test_isupper (unittest官网例子.TestStringMethods.test_isupper) ... FAIL
test_split (unittest官网例子.TestStringMethods.test_split) ... ok
test_upper (unittest官网例子.TestStringMethods.test_upper) ... ok
 This is tearDownClass!!!
======================================================================
FAIL: test_isupper (unittest官网例子.TestStringMethods.test_isupper)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\PythonWorkSpace\AutoTest\unit_test\unittest官网例子.py", line 16, in test_isupper
    self.assertTrue('FOo'.isupper())
AssertionError: False is not true
----------------------------------------------------------------------
Ran 3 tests in 0.002s
FAILED (failures=1)
PS D:\PythonWorkSpace\AutoTest\unit_test>

可以看到,setUpClass、tearDownClass分别只执行了一次。

4、测试套件

  一个测试套件可以包含不用的测试用例,即可以把不同的测试用例统一放到一个套件里面,这样就可以通过执行套件来分别执行不同的用例。这一系列行为可以通过TestSuites、TestLoader和TestRunner类来实现。
首先,创建一个新的用例

import unittest
import math

class TestStringMethods_1(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("\n This is setUPClass in unittest_2!!!")

    @classmethod
    def tearDownClass(cls):
        print("\n This is tearDownClass unittest_2!!!")

    def test_sum(self):
        self.assertEqual(sum([i for i in range(1,5,2)]), 4)

    def test_pow(self):
        self.assertEqual(math.pow(2,3), 8)

if __name__ == '__main__':
    unittest.main(verbosity = 2)

接下来通过TestSuites创建一个测试套件,如下:

from unittest官网例子 import TestStringMethods
from unittest_2 import TestStringMethods_2
import unittest

if __name__ == '__main__':
    #创建测试套件
    suite = unittest.TestSuite()

    #为套件添加用例
    suite.addTest(TestStringMethods("test_upper"))
    suite.addTest(TestStringMethods("test_isupper"))
    suite.addTest(TestStringMethods("test_split"))
    suite.addTest(TestStringMethods_2("test_sum"))
    suite.addTest(TestStringMethods_2("test_pow"))

    #执行测试
    runner = unittest.TextTestRunner()
    runner.run(suite)

执行结果如下:

PS D:\PythonWorkSpace\AutoTest\unit_test> python -m unittest -v run
 This is setUPClass!!!
test_isupper (unittest官网例子.TestStringMethods.test_isupper) ... FAIL
test_split (unittest官网例子.TestStringMethods.test_split) ... ok
test_upper (unittest官网例子.TestStringMethods.test_upper) ... ok
 This is tearDownClass!!!
 
 This is tearDownClass unittest_2!!!
======================================================================
FAIL: test_isupper (unittest官网例子.TestStringMethods.test_isupper)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\PythonWorkSpace\AutoTest\unit_test\unittest官网例子.py", line 16, in test_isupper
    self.assertTrue('FOo'.isupper())
AssertionError: False is not true
----------------------------------------------------------------------
Ran 5 tests in 0.003s
FAILED (failures=1)
PS D:\PythonWorkSpace\AutoTest\unit_test>

还可以用TestLoader和TextTestRunner来创建和运行测试套件,如下:

from unittest官网例子 import TestStringMethods
from unittest_2 import TestStringMethods_2
import unittest

if __name__ == '__main__':
    #从测试用例中获取所有测试
    test_1 = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods)
    test_2 = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods_2)

    #创建测试套件
    suite = unittest.TestSuite([test_1, test_2])

    #执行测试
    runner = unittest.TextTestRunner()
    runner.run(suite)

执行结果

PS D:\PythonWorkSpace\AutoTest\unit_test> python -m unittest -v run
 This is setUPClass!!!
test_isupper (unittest官网例子.TestStringMethods.test_isupper) ... FAIL
test_split (unittest官网例子.TestStringMethods.test_split) ... ok
test_upper (unittest官网例子.TestStringMethods.test_upper) ... ok
 This is tearDownClass!!!
 This is setUPClass in unittest_2!!!
test_pow (unittest_2.TestStringMethods_2.test_pow) ... ok
test_sum (unittest_2.TestStringMethods_2.test_sum) ... ok
 This is tearDownClass unittest_2!!!
======================================================================
FAIL: test_isupper (unittest官网例子.TestStringMethods.test_isupper)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\PythonWorkSpace\AutoTest\unit_test\unittest官网例子.py", line 16, in test_isupper
    self.assertTrue('FOo'.isupper())
AssertionError: False is not true
----------------------------------------------------------------------
Ran 5 tests in 0.003s
FAILED (failures=1)
PS D:\PythonWorkSpace\AutoTest\unit_test>
HTMLTestRunner

  可以应用Wai Yip Tung编写的unittest的扩展HTMLTestRunner来实现。HTMLTestRunner是基于python2写的,所以下载好HTMLTestRunner.py文件后,需要做一些修改。

1、HTMLTestRunner.py下载

  打开下载网址,直接将内容复制,保存为HTMLTestRunner.py文件,并将文件放到python安装目录的Lib文件夹下。下载网址:
http://tungwaiyip.info/software/HTMLTestRunner_0_8_2/test_HTMLTestRunner.py
  

2、修改HTMLTestRunner文件

  需要对文件做出一些修改,将对应行的内容修改成如下内容:

# 第94行      import io
# 第539行    self.outputBuffer = io.StringIO()
# 第631行    print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime))
# 第642行    if not cls in rmap:
# 第766行    uo = o
# 第772行    ue = e
# 第690行     self.stream.write(output)
3、测试并生成报告
from unittest官网例子 import TestStringMethods
from unittest_2 import TestStringMethods_2
import unittest
import HTMLTestRunner

if __name__ == '__main__':
    #从测试用例中获取所有测试
    test_1 = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods)
    test_2 = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods_2)

    #创建测试套件
    suite = unittest.TestSuite([test_1, test_2])

    # 获取输出报告文件的目录路径
    dir = os.getcwd()

    # 打开报告文件
    # outfile = open(dir + "\TestReport.html", "w")

    #执行测试
    with open(dir + "\TestReport.html", "w", encoding="utf-8") as outfile:
        runner = HTMLTestRunner.HTMLTestRunner(
                     stream=outfile,
                     title='Test Report',
                     description='测试报告'
                     )
        runner.run(suite)

执行文件生成的测试报告如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值