Python学习3.0使用Unittest框架运行测试用例
使用Unittest框架运行测试用例
- Unittest 框架
- 1.Unittest 单元测试框架的组成
- 2.TestCase 编写测试用例
- 3.跳过要执行的测试用例
- 4.引入TestFixture
- @classmethod 装饰器
- 5.Unittest的断言
- 6.TestSuite和TestRunner(不支持并发结果合并)
- 1)运行时自动触发unittest.main()逻辑,需修改 PyCharm 配置
- 2)TestSuite添加测试用例的3种方式
- 3)TestLoader添加测试用例
- 7. 生成测试报告TestResult
- 1)TextTestRunner生成的文本格式报告
- 2)HTMLTestRunner_PY3生成的报告(好用,需安装)
- 8. 框架进行并发(unittest不适合,使用pytest)
Unittest 框架
作用: 管理测试用例、断言、生成测试报告
1.Unittest 单元测试框架的组成
Unittest 组成部分 | 作用 |
---|---|
test fixture | 测试固定组件,unittest框架中,一些有固定用法的组件 |
test case | 测试用例,被执行测试的最小单元 |
test suite | 测试套件,它是一个用例集,用来汇总应该一起执行的测试用例 |
test runner | 测试运行器,它是一个设计测试执行方式的元件,主要对用户提供了输出结果的展现方式。它可以用图标、文本、html等方式来展现测试结果 |
2.TestCase 编写测试用例
- 简单执行一个用例 import unittest
Unittest运行的测试用例,必须是继承了 unittest.TestCase 的方法,并且这个方法默认以 test 开头
默认执行顺序是,在test以后,按照ASCII码比对大小
---- 导入包 unittest
import unittest---- 创建继承 unittest.TestCase 的类
class FirstDemo(unittest.TestCase): ------- 固定写法,必须继承def test01(self):print("执行用例一")def test02(self):print("执行用例二")if __name__ == '__main__':unittest.main() ----- 固定写法,调用底层逻辑main执行用例,查看下图
- 执行结果(左侧为执行情况,右侧为执行输出和执行时间)
- 若想更换执行时的测试运行程序
3.跳过要执行的测试用例
方法:使用装饰器 @unitest.skip(“备注”)
- 案例
---- 导入包 unittest
import unittest---- 创建继承 unittest.TestCase 的类
class FirstDemo(unittest.TestCase): ------- 固定写法,必须继承def test01(self):print("执行用例一")@unittest.skip("跳过用例二") ------ @unitest.skipdef test02(self): print("执行用例二")if __name__ == '__main__':unittest.main()
- 运行结果:跳过的内容会被标注
4.引入TestFixture
TestFixture一般都是写在运行的测试用例的类中,作用该类的类方法或者实例方法而存在
运行顺序:setUpClass() - - - 反复执行{ setUp()、测试用例test、tearDown() } - - - tearDownClass()|
TestFixture常用方法 | 应用场景 |
---|---|
setUp() | 每运行一个测试用例之前,先运行的函数。主要用于设置一些配置信息,静态属性等 |
setUpClass() | 类方法,必须和@classmethod装饰器结合使用,实例化类后,会自动运行的方法主要用于实例化类、设置某些环境配置如数据库连接配置等 |
tearDown() | 每运行完一个测试用例之后,后运行的函数。主要用于销毁每个测试用例之间的数据,释放资源,还原数据 |
tearDownClass() | 类中的代码全部运行完成后,会自动运行的方法,必须和@classmethod装饰器结合使用。主要用于销毁类级别的资源,还原数据 |
@classmethod 装饰器
类方法:被 @classmethod 装饰的函数,可以不用实例化类,就能够访问呢
举例:class A: pass - - - 实例化: A().aa - - - 不实例化: A.bb
在 unittest 当中 @classmethod 是 setUpClass() 和 tearDownClass() 的固定写法
- 案例:测试加法运算
# 导入unittest模块
import unittest# 创建要执行的测试类
class add_doing:def Calculator(self,x,y):return x + y# 创建unittest的测试类
class TestPlus(unittest.TestCase):add = None# 编写测试固件def setUp(self): # 如果不想写内容可以写pass跳过self.x = 1self.y = 1print("运行了setUp")@classmethoddef setUpClass(cls):cls.add = add_doing()print("运行了setUpClass")def tearDown(self): # 和setUp一一对应del self.xdel self.yprint("运行了tearDown")@classmethoddef tearDownClass(cls): # 和setUpClass一一对应del cls.addprint("运行了tearDownClass")# 测试用例,测试要执行的测试类def test01(self):print(self.add.Calculator(2,4))def test02(self):print(self.add.Calculator(5,6))def test03(self):print(self.add.Calculator(self.x,self.y))if __name__ == '__main__':unittest.main()
- 运行结果
5.Unittest的断言
Unittest断言方法 | 作用 |
---|---|
assertEqual(a,b) | 检査 a和b是否相等 |
assertTrue(x) | 检查x是不是一个True |
assertls(a,b) | 检查a和b是不是完全一样 |
assertlsNone(x) | 检查x是不是一个None |
assertIn(a,b) | 检查a是不是b的子集 |
assertlsInstance(a,b) | 检查a、b两个对象,实例类型是否相同 |
- 案例
import unittestclass TestAssert(unittest.TestCase):
----- unittest的断言是在TestCase中实现的,所以必须继承TestCase之后,才能够使用unittest的断言方法def setUp(self):self.l1,self.l2 = [1,2],[1,2]self.a = 1self.b = 1def test01_assertEqual(self):----- 断言self.a和self.b是否完全相等self.assertEqual(self.a, self.b)self.assertEqual(self.l1, self.l2)def test02_assertTrue(self):----- assertTrue(x)可以根据传入的数据,判断真或者假(True,False)# assertTrue(0)是False# assertTrue(None)是False# assertTrue(1)是Trueself.assertTrue(self.a)self.assertTrue(self.l1)def test03_assertIs(self):----- 断言self.a和self.b是否完全相同self.assertIs(self.a, self.b)self.assertIs(self.l1, self.l2) # Falsedef test04_assertIn(self):----- 断言a是否是 l1 的子集self.assertIn(self.a, self.l1) # Trueself.assertIn(self.l1, self.l2) ----- False 列表不能做子集运算def test05_assertIsNone(self):----- 断言是不是Noneself.assertIsNone(self.a) # Falseself.assertIsNone(None) # Truedef test06_assertIsInstance(self):----- 断言类型self.assertIsInstance(self.a, int) # 判断a是不是整形 Trueself.assertIsInstance(self.l1, int) # False
6.TestSuite和TestRunner(不支持并发结果合并)
test suite | 测试套件,它是一个用例集,用来汇总应该一起执行的测试用例 |
test runner | 测试运行器,它是一个设计测试执行方式的元件,主要对用户的输出结果进行展示 |
- 案例
# 导包
import unittest# 创建要执行的测试用例类
class TestSuiteDemo(unittest.TestCase):def test01(self):print("执行的测试用例test01,a")def test02(self):print("执行的测试用例test02,b")class TestSuiteDemo2(unittest.TestCase):def test01(self):print("执行的测试用例test01,张三")def test02(self):print("执行的测试用例test02,李四")if __name__ == '__main__':
# 实例化测试套件suite = unittest.TestSuite()# 将测试用例添加到测试套件中suite.addTest(TestSuiteDemo('test01'))suite.addTest(TestSuiteDemo2('test02'))
# 实例化runnerrunner = unittest.TextTestRunner()# 使用runner运行测试用例生成测试报告runner.run(suite)
1)运行时自动触发unittest.main()逻辑,需修改 PyCharm 配置
- 修改配置后的运行结果
2)TestSuite添加测试用例的3种方式
TestSuite添加测试用例的3种方式 | 写法举例(括号内命名可变) |
---|---|
suite.addTest 单个添加 | suite.addTest(TestSuiteDemo(‘test01’)) |
suite.addTests 多个添加 | suie.addTests([TestSuiteDemo(‘test01’),TestSuiteDemo2(‘test02’)]) |
TestLoader | 下面会重点介绍 |
3)TestLoader添加测试用例
两种方法 | 解释 |
---|---|
TestLoader().discover(“./”,“*.py”) | 根据执行的路径来寻找指定py文件中的测试用例,加载到测试套件当中 |
TestLoader().loadTestsFromTestCase(测试用例类名) | 根据测试用例的类名来加载测试用例 |
- 案例
# 导入模块
import unittest# 创建测试用例的类
class TestSuiteDemo1(unittest.TestCase):def test01(self):print("执行的测试用例test01,a")def test02(self):print("执行的测试用例test02,b")class TestSuiteDemo2(unittest.TestCase):def test01(self):print("执行的测试用例test01,张三")def test02(self):print("执行的测试用例test02,李四")class TestSuiteDemo3(unittest.TestCase):def test01(self):print("单元测试")def test02(self):print("集成测试")def test03(self):print("系统测试")
- 方法一:TestLoader().discover(“./”,“*.py”)
if __name__ == '__main__':------ 实例化testloaderstl = unittest.TestLoader()--- 使用testloader的对象tl来加载测试用例成测试套件suite = tl.discover('./','four_unittest.py')runner = unittest.TextTestRunner()runner.run(suite)
- 运行结果
- 方法二:TestLoader().loadTestsFromTestCase(测试用例类名)
if __name__ == '__main__':# 实例化testloaderstl = unittest.TestLoader()# 使用testloader的对象tl来加载测试用例成测试套件suite = tl.loadTestsFromTestCase(TestSuiteDemo3)runner = unittest.TextTestRunner()runner.run(suite)
- 运行结果
7. 生成测试报告TestResult
使用unittest运行测试用例,最后会生成测试报告,我们可以把测试报告保存到文件中
1)TextTestRunner生成的文本格式报告
- 查看TextTestRunner底层代码
- 案例
if __name__ == '__main__':tl = unittest.TestLoader()suite = tl.discover('./','four_unittest.py')--- 使用TextTestRunner生成测试报告--- 1.打开要保存的测试报告文件,‘w’是写入的意思with open('./result.txt',mode='w') as f:--- 2.实例化TextTestRunnerrunner = unittest.TextTestRunner(f, verbosity=3,descriptions=True)--- 3.使用runner运行测试套件runner.run(suite)
- 运行结果
2)HTMLTestRunner_PY3生成的报告(好用,需安装)
HTMLTestRunner_Py3是一个能生成HTML格式的,显示更友好的测试报告,它不仅能够显示出测试用例的执行结果,还能跟踪测试用例执行失败的原因
HTMLTestRunner_PY3下载安装地址
- 安装成功结果显示
- 案例
from Scripts.HTMLTestRunner import HTMLTestRunnerif __name__ == '__main__':tl = unittest.TestLoader()suite = tl.discover('./','four_unittest.py')--- 使用HTMLTestRunner生成测试报告--- 1.打开要保存的测试报告文件,‘wb’是写入的意思with open('./result.html',mode='wb') as f:--- 2.实例化HTMLTestRunnerrunner = HTMLTestRunner(f,verbosity=2,title='演示HTML测试报告')--- 3.使用runner运行测试套件runner.run(suite)
- 运行结果
- 浏览器展示
8. 框架进行并发(unittest不适合,使用pytest)
使用多线程运行,可以并行执行测试用例,提升测试速度
框架 | 是否推荐多线程 | 推荐方式 |
---|---|---|
unittest | 不推荐手动用 threading 或 threadpool | 使用 concurrent.futures + 手动结果汇总,或改用 pytest |
pytest | 强烈推荐使用并发执行 | 使用 pytest-xdist 插件(基于多进程,非多线程) |
- unittest使用concurrent.futures多线程举例(适用于老项目)
from concurrent.futures import ThreadPoolExecutor
import unittestdef run_test_case(test):result = unittest.TestResult()test(result)return resultif __name__ == '__main__':# 拆分测试用例suite = unittest.TestLoader().discover('./','*.py')test_cases = [test for test in suite]with ThreadPoolExecutor(max_workers=4) as executor:futures = [executor.submit(run_test_case, test) for test in test_cases]# 汇总结果total_result = unittest.TestResult()for future in futures:result = future.result()# 手动合并结果(注意线程安全)total_result.errors.extend(result.errors)total_result.failures.extend(result.failures)total_result.testsRun += result.testsRun