一、快速入门
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*开头,一个单元编写一个测试用例。
- 编写测试用例 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...')
- 编写测试用例 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、运行测试用例
运行测试用例并生成测试报告。这一步可以拆分成如下步骤:
- 实例化一个测试套件
- 加载测试用例到测试套件
- 运行测试套件
- 生成测试报告
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 官网下载的第三方插件:HTMLTestRunner、BeautifulReport,可将测试结果保存成 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:官方文档