一、快速入门
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)编写注册功能测试用例test_register.py如下:
"""
注册功能测试用例
"""
import unittest
class TestRegister(unittest.TestCase):
def setUp(self) -> None:
print('setUp:打开注册页面')
def tearDown(self) -> None:
print('tearDown:关闭注册页面')
def test01(self):
print('test01:注册成功用例')
def test02(self):
print('test02:注册失败用例')
(2)编写登录功能测试用例test_login.py如下:
"""
登录功能测试用例
"""
import unittest
class TestLogin(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print('setUpClass:打开登录页面')
@classmethod
def tearDownClass(cls) -> None:
print('tearDownClass:关闭登录页面')
def test03(self):
print('test03:登录成功用例')
def test04(self):
print('test04:登录失败用例')
(3)项目目录结构如下:
3、运行测试用例
项目根目录中,新建程序主入口run.py文件,运行测试用例并生成测试报告。这一步可以拆分成如下步骤:
(1)实例化一个测试套件
(2)加载测试用例到测试套件
(3)运行测试套件
(4)生成测试报告
"""
程序主入口
"""
import unittest
# 封装测试套件方法
def suite():
# 实例化测试套件
testsuite = unittest.TestSuite()
# TestLoader测试加载器
loader = unittest.TestLoader()
tests = loader.discover(start_dir='testcases', pattern='test*.py')
# 加载测试用例到测试套件
testsuite.addTests(tests)
return testsuite
# 运行测试套件
if __name__ == '__main__':
suite = suite()
# 测试运行器,生成文本格式测试报告
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
控制台打印日志详细程度参数 :verbosity
- verbosity=0 (静默模式)::你只能获得总的测试用例数和总的结果。比如 总共100个 失败20 成功80
- verbosity=1 (默认模式): 非常类似静默模式 只是在每个成功的用例前面有个“.” 每个失败的用例前面有个 “E”
- verbosity=2 (详细模式):测试结果会显示每个测试用例的所有相关的信息
关于测试套件,测试加载器,测试运行器会在下文API中详细说明,为了快速入门,这里只要会初步使用就行。
项目目录结构如下:
运行main方法,注册功能的测试用例test_register.py,登录功能的测试用例test_login.py中的所有子用例都执行了。
4、使用测试套件加载测试用例
测试套件TestSuite对象的方法有addTest逐条加载测试用例(测试套件),addTests加载多条测试用例(测试套件)。也可以结合测试加载器TestLoader对象的方法一起用来加载多条测试用例。
项目根目录中,新建run_suite.py文件如下,其中列举了7种加载测试用例的方法。
"""
测试套件加载测试用例的方法
"""
import unittest
from testcases import test_register, test_login
# addTest(test) 添加一个TestCase或者TestSuite到测试套件中,test必须是已经实例化的对象。
# 加载单个测试用例
def suite1():
testsuite = unittest.TestSuite()
test = test_register.TestRegister('test01')
testsuite.addTest(test)
return testsuite
# 逐个加载测试用例
def suite11():
testsuite = unittest.TestSuite()
test1 = test_register.TestRegister('test01')
test2 = test_register.TestRegister('test02')
testsuite.addTest(test1)
testsuite.addTest(test2)
return testsuite
# 加载单个测试套件
def suite12():
testsuite = unittest.TestSuite()
test2 = test_register.TestRegister('test02')
testsuite.addTest(suite1())
testsuite.addTest(test2)
return testsuite
# addTests(tests) 添加多个TestCase或者TestSuite到测试套件中,tests必须是已经实例化,并且可迭代的对象.
# 加载多个测试用例
def suite2():
testsuite = unittest.TestSuite()
tests = [test_login.TestLogin('test03'), test_login.TestLogin('test04')]
testsuite.addTests(tests)
return testsuite
# 加载多个测试套件
def suite21():
testsuite = unittest.TestSuite()
tests = [suite1(), suite2()]
testsuite.addTests(tests)
return testsuite
# TestLoader() 测试加载器
# loadTestsFromTestCase(testCaseClass) 加载某个测试类中的所有测试用例
def suite3():
testsuite = unittest.TestSuite()
loader = unittest.TestLoader()
tests = loader.loadTestsFromTestCase(test_register.TestRegister)
testsuite.addTests(tests)
return testsuite
# loadTestsFromModule(module) 加载某个模块中的所有测试用例
def suite4():
testsuite = unittest.TestSuite()
loader = unittest.TestLoader()
tests = loader.loadTestsFromModule(test_register)
testsuite.addTests(tests)
return testsuite
# loadTestsFromName(name) 根据给定的字符串来获取测试用例套件,字符串可以是模块名,测试类名,测试类中的测试方法名
def suite5():
testsuite = unittest.TestSuite()
loader = unittest.TestLoader()
tests = loader.loadTestsFromName('testcases.test_register')
testsuite.addTests(tests)
return testsuite
# loadTestsFromNames(names) 与name功能相同,只不过接受的是字符串列表
def suite6():
testsuite = unittest.TestSuite()
loader = unittest.TestLoader()
tests = loader.loadTestsFromNames(['testcases.test_register', 'testcases.test_login'])
testsuite.addTests(tests)
return testsuite
# discover(start_dir, pattern='test*.py') 根据指定的目录和匹配规则,递归所有子目录模糊查询加载测试用例
def suite7():
testsuite = unittest.TestSuite()
loader = unittest.TestLoader()
tests = loader.discover(start_dir='./testcases', pattern='test*.py')
testsuite.addTests(tests)
return testsuite
# 运行测试套件
if __name__ == '__main__':
suite = suite1()
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
5、生成HTML格式的测试报告
测试运行器TextTestRunner以文本的格式输出测试结果,不方便展示我们测试成果,下面介绍2种方法生成HTML格式的测试报告。
(1)HTMLTestRunner
第三方插件,可将测试结果保存成html文件,从PyPI官网下载的原文件不能直接使用,原文件是基于Python2的版本,需要修改成兼容Python3才行,建议直接百度一份别人修改好后可以使用的文件。
(2)BeautifulReport
第三方插件,可将测试结果保存成html文件,从PyPI官网下载后可以直接使用,推荐使用。
项目根目录下新建run_report.py文件如下:
"""
生成测试报告的几种方式
"""
import unittest
# import HTMLTestRunner
from BeautifulReport import BeautifulReport
def suite():
testsuite = unittest.TestSuite()
loader = unittest.TestLoader()
tests = loader.discover(start_dir='./testcases', pattern='test*.py')
testsuite.addTests(tests)
return testsuite
# TextTestRunner
# if __name__ == '__main__':
# suite = suite()
# with open(file='./report/report.txt', mode='a') as file:
# runner = unittest.TextTestRunner(verbosity=2, stream=file)
# runner.run(suite)
# HTMLTestRunner
# if __name__ == '__main__':
# suite = suite()
# with open(file='HTMLReport.html', mode='w') as file:
# runner = HTMLTestRunner.HTMLTestRunner(stream=file, verbosity=2)
# runner.run(suite)
# BeautifulReport
if __name__ == '__main__':
suite = suite()
result = BeautifulReport(suite)
result.report(report_dir='./report', filename='breport.html', description='注册和登录功能测试报告')
运行run_report.py文件,生成测试报告如下:
使用chrome浏览器打开测试报告如下:
6、命令行模式运行
上文说的都是通过main方法来运行测试用例,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
以上文的注册功能测试用例test_register.py,登录功测试用例test_login.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:上下文管理器
断言:assertEqua,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可以是test case 或者 test suite
reference:
unittest — Unit testing framework — Python 3.8.13 documentation