Python 模块 —— 单元测试框架 unittest

一、快速入门

1、unittest介绍

unittest 是 Python 内置的单元测试框架,不需要安装可以直接调用。其中几个核心概念如下:
Test Fixture:

  • 测试脚手架,用来完成测试前置处理,后置处理。如测试前的环境准备,测试完成后环境销毁。
  • 测试函数级别:setUp,tearDown
  • 测试类级别:setUpClass,tearDownClass

Test Case:

  • 测试用例, 一个测试用例是一个独立的测试单元,新建测试用例需要继承 unittest.TestCase。

Test Suite:

  • 测试套件,用来收集测试用例或者测试套件。suite = unittest.TestSuite()

Test Loader:

  • 测试加载器,用来将测试用例加载到测试套件中。unittest.TestLoader()

Test Runner:

  • 测试运行器,用来运行测试并输出测试结果。如将测试结果输出文本格式:unittest.TextTestRunner()

2、编写测试用例

新建测试用例需要继承 unittest.TestCase,测试用例以test*开头,一个单元编写一个测试用例。

  1. 编写测试用例 TestCase1.py 如下:
import unittest


class TestCase1(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        print('setUpClass...')

    def setUp(self) -> None:
        print('setUp...')

    def test1(self):
        print('test1...')

    def test2(self):
        print('test2...')

    def tearDown(self) -> None:
        print('tearDown...')

    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass...')


class TestCase2(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        print('setUpClass...')

    def setUp(self) -> None:
        print('setUp...')

    def test1(self):
        print('test3...')

    def test2(self):
        print('test4...')

    def tearDown(self) -> None:
        print('tearDown...')

    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass...')

  1. 编写测试用例 TestCase2.py 如下:
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from BeautifulReport import BeautifulReport


class TestCase(unittest.TestCase):
    driver = None

    @classmethod
    def setUpClass(cls) -> None:
        cls.driver = webdriver.Chrome()
        cls.driver.get('https://www.baidu.com')
        cls.driver.maximize_window()

    @BeautifulReport.add_test_img('搜索前', '搜索后')
    def test(self):
        """测试百度搜索"""
        self.driver.save_screenshot('img/搜索前.png')
        self.driver.find_element(By.ID, 'kw').send_keys('selenium')
        self.driver.find_element(By.ID, 'su').click()
        WebDriverWait(self.driver, 10).until(expected_conditions.title_contains('selenium'), '等待搜索结果超时!!!')
        self.driver.save_screenshot('img/搜索后.png')

    @classmethod
    def tearDownClass(cls) -> None:
        cls.driver.quit()

3、使用测试套件加载测试用例

测试套件 TestSuite 对象的方法有 addTest 逐条加载测试用例(测试套件),addTests 加载多条测试用例(测试套件)。也可以结合测试加载器 TestLoader 对象的方法一起用来加载多条测试用例。

import unittest
import TestCase1


# 逐个加载测试用例
def suite1():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 加载测试用例到测试套件
    testsuite.addTest(TestCase1.TestCase1('test1'))
    testsuite.addTest(TestCase1.TestCase1('test2'))

    return testsuite


# 加载多个测试用例
def suite2():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 加载测试用例到测试套件
    cases = [TestCase1.TestCase2('test3'), TestCase1.TestCase2('test4')]
    testsuite.addTests(cases)

    return testsuite


# 逐个加载测试套件
def suite3():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 加载测试套件到测试套件
    testsuite.addTest(suite1())
    testsuite.addTest(suite2())

    return testsuite


# 加载多个测试套件
def suite4():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 加载测试套件到测试套件
    suites = [suite1(), suite2()]
    testsuite.addTests(suites)

    return testsuite


# TestLoader() 测试加载器
# loadTestsFromTestCase(testCaseClass) 加载某个测试类中的所有测试用例
def suite5():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 实例化测试加载器
    loader = unittest.TestLoader()
    # 加载某个测试类中的所有测试用例
    testsuite.addTest(loader.loadTestsFromTestCase(TestCase1.TestCase1))

    return testsuite


# loadTestsFromModule(module) 加载某个模块中的所有测试用例
def suite6():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 实例化测试加载器
    loader = unittest.TestLoader()
    # 加载某个模块中的所有测试用例
    testsuite.addTest(loader.loadTestsFromModule(TestCase1))

    return testsuite


# loadTestsFromName(name) 根据指定的字符串加载测试用例,字符串可以是模块名、测试类名、测试类中的测试方法名
def suite7():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 实例化测试加载器
    loader = unittest.TestLoader()
    # 模块名
    testsuite.addTest(loader.loadTestsFromName('TestCase1'))
    # 测试类名
    testsuite.addTest(loader.loadTestsFromName('TestCase1.TestCase1'))
    # 测试类中的测试方法名
    testsuite.addTest(loader.loadTestsFromName('TestCase1.TestCase1.test1'))

    return testsuite


# loadTestsFromNames(names) 与name功能相同,区别是接收的参数是字符串列表
def suite8():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 实例化测试加载器
    loader = unittest.TestLoader()
    # 模块名、测试类名、测试类中的测试方法名
    testsuite.addTest(loader.loadTestsFromNames(['TestCase1', 'TestCase.TestCase1', 'TestCase.TestCase1.test1']))

    return testsuite


# discover(start_dir, pattern) 根据指定的目录和匹配规则,递归所有子目录模糊查询加载测试用例
def suite9():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 实例化测试加载器
    loader = unittest.TestLoader()
    # 根据指定的目录和匹配规则,递归所有子目录模糊查询加载测试用例
    testsuite.addTest(loader.discover(start_dir='./', pattern='test*.py'))

    return testsuite

4、运行测试用例

运行测试用例并生成测试报告。这一步可以拆分成如下步骤:

  1. 实例化一个测试套件
  2. 加载测试用例到测试套件
  3. 运行测试套件
  4. 生成测试报告
import unittest
import TestCase1


# discover(start_dir, pattern) 根据指定的目录和匹配规则,递归所有子目录模糊查询加载测试用例
def suite():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 实例化测试加载器
    loader = unittest.TestLoader()
    # 根据指定的目录和匹配规则,递归所有子目录模糊查询加载测试用例
    testsuite.addTest(loader.discover(start_dir='./', pattern='test*.py'))

    return testsuite


# 运行测试套件
if __name__ == '__main__':
    suite = suite()
    # 测试运行器,存储测试结果,并以文本的形式输出
    runner = unittest.TextTestRunner(verbosity=2)  # 控制台打印日志详细程度参数:verbosity=0(静默模式)、verbosity=1(默认模式)、verbosity=2(详细模式)
    runner.run(suite)

控制台打印日志详细程度参数:verbosity

  • verbosity=0(静默模式):只能获得总的测试用例数和总的结果。比如:总共100,成功80,失败20。
  • verbosity=1(默认模式):非常类似静默模式,只是在每个成功的用例前面有个“.”,每个失败的用例前面有个“E”。
  • verbosity=2(详细模式):测试结果会显示每个测试用例的所有相关信息。

5、生成 HTML 格式的测试报告

测试运行器 TextTestRunner 以文本的格式输出测试结果,不方便展示测试成果,从 PyPI 官网下载的第三方插件:HTMLTestRunnerBeautifulReport,可将测试结果保存成 html 文件。

import unittest
from HtmlTestRunner import HTMLTestRunner
from BeautifulReport import BeautifulReport


def suite():
    # 实例化测试套件
    testsuite = unittest.TestSuite()
    # 实例化测试加载器
    loader = unittest.TestLoader()
    # 根据指定的目录和匹配规则,递归所有子目录模糊查询加载测试用例
    testsuite.addTest(loader.discover(start_dir='./', pattern='TestCase2.py'))

    return testsuite


# # TextTestRunner
# if __name__ == '__main__':
#     suite = suite()
#     with open(file='../reports/MyReport.txt', mode='w') as file:
#         # 测试运行器,存储测试结果,并以文本的形式输出
#         runner = unittest.TextTestRunner(verbosity=2)  # 控制台打印日志详细程度参数:verbosity=0(静默模式)、verbosity=1(默认模式)、verbosity=2(详细模式)
#         runner.run(suite)

# # HTMLTestRunner
# if __name__ == '__main__':
#     suite = suite()
#     # combine_reports:将测试报告合并为单个测试报告(默认情况下,将为每个TestCase生成单独的测试报告)。
#     # output:测试报告保存路径。
#     # report_name:测试报告名称。
#     # add_timestamp:测试报告名称加上时间戳。
#     runner = HTMLTestRunner(combine_reports=True,
#                             output='../reports',
#                             report_name='MyReport',
#                             add_timestamp=False)
#     runner.run(suite)

# BeautifulReport
if __name__ == '__main__':
    suite = suite()
    result = BeautifulReport(suite)

    # filename -> 测试报告名称, 如果不指定默认文件名为report.html
    # description -> 测试报告用例名称展示
    # report_dir = '.' -> 报告文件写入路径
    # theme = 'theme_default' -> 报告主题样式:theme_default、theme_cyan、theme_candy、theme_memories

    # add_test_img(*pargs)
    # 可以在测试用例上挂载一个装饰器, 实例内容如下:
    # 默认存放的图片路径是img, 需要在当前测试项目的启动路径下, 创建一个img文件夹;
    # 传递给装饰器的图片, 在运行测试前可以不存在, 运行测试之后生成即可;
    # 当文件在报告中展示后, 想要看到原图, 可以点击报告中的缩略图查看完整的截图.

    result.report(filename='测试报告',
                  description='test',
                  report_dir='../reports',
                  theme='theme_memories')

使用 chrome 浏览器打开测试报告如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6、命令行模式运行

unittest 也可以通过命令行模式运行测试用例。几种常见的命令如下:

  • python -m unittest:探索性测试,默认搜索当前目录及其子目录中,所有 test*.py 模式的测试文件。
  • python -m unittest test_module1
  • python -m unittest test_module1 test_module2
  • python -m unittest test_module.TestClass
  • python -m unittest test_module.TestClass.test_method
  • python -m unittest tests/test_something.py

打开 Terminal 终端,进入路径 testcases 中,输入命令:python -m unittest
在这里插入图片描述

7、跳过测试(测试函数/测试类)

  • @unittest.skip(reason),跳过被此装饰器装饰的测试。reason 为跳过测试的原因。
  • @unittest.skipIf(condition, reason),当 condition 为真时,跳过被装饰的测试。
  • @unittest.skipUnless(condition, reason),跳过被装饰的测试,除非 condition 为真。
  • TestCase.skipTest(),直接跳过测试,抛出 SkipTest 异常。
  • @unittest.expectedFailure,标记测试用例预期失败。

8、注意事项

  • 测试用例执行顺序:根据 ASCII 码的顺序加载,数字与字母的顺序为:0-9,A-Z,a-z。
  • 若 setUp() 方法引发异常,测试框架会认为测试发生了错误,因此测试方法不会被运行。
  • 若 setUp() 成功运行,无论测试方法是否成功,都会运行 tearDown()。
  • 被跳过的测试的 setUp() 和 tearDown() 不会被运行。
  • 被跳过的类的 setUpClass() 和 tearDownClass() 不会被运行。
  • 被跳过的模组的 setUpModule() 和 tearDownModule() 不会被运行。

二、API

1、TestCase 测试用例

  • setUp:前置处理,函数级别,此方法会在调用每个测试函数之前被调用。除了 AssertionError 或 SkipTest,此方法所引发的任何异常都将被视为错误而非测试失败。默认不做任何处理。
  • tearDown:后置处理,函数级别,此方法会在调用每个测试函数之后被调用。无论测试是否成功,只要 setUp 执行成功,都会执行该方法。除了 AssertionError 或 SkipTest,此方法所引发的任何异常都将被视为错误而非测试失败。默认不做任何处理。
  • setUpClass:前置处理,测试类级别,在一个单独类中的测试运行之前被调用的类方法。使用 @classmethod 装饰器。
  • tearDownClass:后置处理,测试类级别,在一个单独类中的测试运行之后被调用的类方法。使用 @classmethod 装饰器。
  • run:运行测试,并将测试结果保存到 TestResult 对象中。
  • skipTest:在测试方法或 setUp() 执行期间调用此方法将跳过当前测试。
  • fail:使用给定消息立即失败。
  • id:返回测试方法的完整名称,包括模块名和类名。
  • subTest:上下文管理器。
  • 断言:assertEqual,assertNotEqual,assertTrue,assertFalse,…
  • 异常:assertRaises,assertWarns,assertLogs,…

2、TestSuite 测试套件

  • addTest(test):添加一个 TestCase 或者 TestSuite 到测试套件中,test 必须是已经实例化的对象。
  • addTests(tests):添加多个 TestCase 或者 TestSuite 到测试套件中,tests 必须是已经实例化,并且可迭代的对象。
  • run(result):运行测试,并将测试结果保存到 TestResult 对象中。

3、TestLoader 测试加载器

  • loadTestsFromTestCase(testCaseClass):加载某个测试类中的所有测试用例。
  • loadTestsFromModule(module):加载某个模块中的所有测试用例。
  • loadTestsFromName(name):根据指定的字符串加载测试用例,字符串可以是模块名、测试类名、测试类中的测试方法名。
  • loadTestsFromNames(names):与 name 功能相同,区别是接收的参数是字符串列表。
  • discover(start_dir, pattern=‘test*.py’):根据指定的目录和匹配规则,递归所有子目录模糊查询加载测试用例。

4、TestResult 测试结果

用来存储 TestCase.run(),TestSuite.run() 的测试结果到 result 中,可以通过扩展这个基类的方式自定义测试报告。如:HTMLTestRunner。

5、TextTestRunner 测试运行器

run(test):存储测试结果,并以文本的形式输出,test 可以是 TestCase 或者 TestSuite。


reference:官方文档

猜你喜欢

转载自blog.csdn.net/weixin_56175092/article/details/131896863