【python】了解单元测试框架 unittest 与pytest
unittest
官方参考地址:https://docs.python.org/zh-cn/3/library/unittest.html?highlight=assertequal#module-unittest
1、安装及导入
# unittest是python内置的用于测试代码的模块,无需安装直接导入使用即可
import unittest
2、使用
注意事项:
1、测试文件必须先导入模块 import unittest 2、测试类必须继承 unittest.TestCase 3、测试方法必须由test开头 4、测试类执行统一用unittest.main()
5、setUpClass和tearDownClass 必须使用@classmethod装饰器
6、unittest执行测试用例,默认是根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。
# coding:utf-8 import unittest #模块导入 import requests ''' unittest特点: 1、测试文件必须先导入模块 import unittest 2、测试类必须继承 unittest.TestCase 3、测试方法必须由test开头 4、测试类执行统一用unittest.main() 5、setUpClass和tearDownClass 必须使用@classmethod装饰器 ''' class TestDemo(unittest.TestCase): @classmethod # 所有test运行前运行一次 def setUpClass(cls): print("setUpClass 开始执行测试方法") @classmethod # 所有test运行完运行一次 def tearDownClass(cls): print("tearDownClass 测试方法全部执行完成") # 设置每个test测试开始前需要执行的指令,准备测试环境 def setUp(self): print("setUp 准备环境") # 设置每个test测试完成后需要执行的指令,清理测试环境 def tearDown(self): print("tearDown 环境复原") # 测试方法 def test_step_one(self): self.assertEqual(6, 6, msg="不通过") def test_step_two(self): # 检查两个值的相等性; self.assertEqual([1, 2, 3], [1, 2, 3], "断言比对不一致") # 断言1和1是否相等 def test_step_three(self): # 比较测试值与true self.assertTrue(True, msg="失败原因:比对不一致") # def test_step_four(self): # self.assertEquals([1, 2, 3], [3, 2, 1], msg="比对不一致") if __name__=="__main__": # 提供了一个测试脚本的命令行接口,用来测试继承unittest.TestCase的类中以 test 开头的测试用例 unittest.main()TestDemo
执行结果
unittest.main(verbosity=0) 简洁,只能获得总的测试用例数和总的结果
unittest.main(verbosity=1) 默认,每个成功的用例前面有个“.” ,每个失败的用例前面有个 “E”
unittest.main(verbosity=2) 详情,会显示每个测试用例的所有相关的信息
3、装饰器
@ 是修饰符
跳过测试用例 [不会执行被修饰器修饰的方法,类,setup等]
@unittest.skip(reason)
跳过被此装饰器装饰的测试。 reason 为测试被跳过的原因。
@unittest.skipIf(condition, reason)
当 condition 为真时,跳过被装饰的测试。
@unittest.skipUnless(condition, reason)
跳过被装饰的测试,除非 condition 为真。
@unittest.expectedFailure
将测试标记为预期的失败或错误。 如果测试失败或在测试函数自身(而非在某个 test fixture 方法)中出现错误则将认为是测试成功。 如果测试通过,则将认为是测试失败。
exception unittest.SkipTest(reason)
引发此异常以跳过一个测试。
示例:
# coding:utf-8 import unittest #模块导入 import requests ''' unittest特点: 1、测试文件必须先导入模块 import unittest 2、测试类必须继承 unittest.TestCase 3、测试方法必须由test开头 4、测试类执行统一用unittest.main() 5、setUpClass和tearDownClass 必须使用@classmethod装饰器 ''' class TestDemo(unittest.TestCase): # 测试方法 @unittest.skip("无条件跳过被此装饰器装饰的测试: test_step_one") def test_step_one(self): self.assertEqual(1, 2, "断言比对不一致") # 断言1和1是否相等 @unittest.skipIf(True, "当 condition 为真时,跳过被装饰的测试:test_step_two") def test_step_two(self): # 比较测试值与true self.assertEqual(1, 1, "断言比对不一致") # 断言1和1是否相等 @unittest.skipUnless(False, "跳过被装饰的测试,除非 condition 为真:test_step_three") def test_step_three(self): self.assertEqual(1, 2, "断言比对不一致") # 断言1和1是否相等 @unittest.expectedFailure # 预期失败的情况 def test_step_four(self): # 检查两个值的相等性; self.assertEqual(1, 2, "断言比对不一致") # 断言1和1是否相等 if __name__=="__main__": # 提供了一个测试脚本的命令行接口,用来测试继承unittest.TestCase的类中以 test 开头的测试用例 unittest.main()TestDemo
执行结果
4、断言
pytest 安装及使用
官方参考链接:https://docs.pytest.org/en/7.0.x/
1、安装及导入
# pytest 是python第三方单元测试框架,兼容unittest
pip install pytest
2、使用
注意事项
1、文件名都需要满足test_*.py格式或*_test.py格式。 2、测试类以Test开头,并且不能带有 init 方法,可以包含一个或多个test_开头的函数 3、直接执行pytest.main():自动查找当前目录下,以test_开头的文件或者以_test结尾的py文件 4、断言使用assert
1、自动查找当前目录下,以test_xxx开头或者以xxx_test结尾的py文件 2、main() 可传入执行参数及插件参数(通过[]进行分割,多个参数用逗号分割) 3、pytest.main(['目录名']) # 运行目录及子目录下所有用例 4、pytest.main(['test_xx.py‘]) # 运行指定模块所有用例 5、pytest.main(['test_xx.py::TestClass::test_def']) # 指定模块、类、用例(方法) 6、其他参数配置 -m=xxx: 运行打标签的用例 -reruns=xxx,失败重新运行 -q: 安静模式, 不输出环境信息 -v: 丰富信息模式, 输出更详细的用例执行信息 -s: 显示程序中的print/logging输出 --resultlog=./log.txt 生成log --junitxml=./log.xml 生成xml报告pytest.main() 参数
pytest执行中会出现6个退出code清单 code 0 全部用例执行通过 code 1 全部用例执行完成,存在失败用例 code 2 测试执行过程中人为中断 code 3 测试执行过程发生了内部错误 code 4 pytest 命令行使用错误 code 5 没有发现可用的测试用例文件
运行pytest
# 执行当前目录下的所有用例 > pytest #==== 2 failed, 4 passed, 3 skipped, 1 xfailed in 0.32s ==== # 执行指定模块 > pytest test_run_case_pytest.py #2 failed, 3 skipped, 1 xfailed in 0.26s # 执行指定模块下的指定用例 > pytest test_run_case_pytest.py::TestPy::test_funski #==== 1 skipped in 0.02s ==== # 模糊匹配包含func用例 > pytest -k func test_run_case_pytest.py #==== 2 failed, 4 deselected in 0.25s ====
3、断言
Pytest使用的是python自带的关键字assert来断言【格式:assert 表达式,“预期失败信息”】结果为True 用例成功,False 用例失败
4、修饰器
pytest.skip
# pytest.skip (用于函数内,跳过测试用例) def test_funski(self): for i in range(9): if i > 3: pytest.skip("函数内跳过测试") pass
执行结果:
@pytest.mark.skip # 无条件跳过用例(方法) def test_case1(self): print("\n") assert 2 + 3 == 6, "结果比对不符合要求"
执行结果:
@pytest.mark.skipif(condition="2>0", reason="条件成立 跳过测试") def test_print(self): print("print打印日志")
执行结果:
@pytest.mark.xfail(True, reason="reason") # 跳过预期失败用例,当condition =True 且实际失败则跳过用例 def test_case2(self): assert add(14, 7) == 20, "不满足预期结果"
执行结果:
# coding:utf-8 import pytest from pawd.base.Logger import * def add(a:int, b:int=0): return a+b class TestPy(): ''' 1、文件名都需要满足test_*.py格式或*_test.py格式。 2、测试类以Test开头,并且不能带有 init 方法,可以包含一个或多个test_开头的函数 3、直接执行pytest.main():自动查找当前目录下,以test_开头的文件或者以_test结尾的py文件 :return: ''' def test_func(self): raise IOError("E") # raise 指定异常的类型 IOError def test_myfunc(self): # 使用pytest去断言异常的类型 ,异常一致则用例通过,否则用例败 with pytest.raises(SystemError): self.test_func() def test_logs(self): logs.info("logs打印日志") def test_print(self): print("print打印日志") @pytest.mark.skip # 无条件跳过用例 def test_case1(self): print("\n") assert 2 + 3 == 6, "结果比对不符合要求" @pytest.mark.flaky(reruns=5, reruns_delay=2) def test_case2(self): for i in range(9): # assert add(14, i) == 20, "不满足预期结果" try: assert add(14, i) == 20, "不满足预期结果" except: continue else: logs.debug(i) if __name__ == "__main__": ''' 1、自动查找当前目录下,以test_xxx开头或者以xxx_test结尾的py文件 2、main() 可传入执行参数及插件参数(通过[]进行分割,多个参数用逗号分割) 3、pytest.main(['目录名']) # 运行目录及子目录下所有用例 4、pytest.main(['test_xx.py‘]) # 运行指定模块所有用例 5、pytest.main(['test_xx.py::TestClass::test_def']) # 指定模块、类、用例(方法) 6、其他参数配置 -m=xxx: 运行打标签的用例 -reruns=xxx,失败重新运行 -q: 安静模式, 不输出环境信息 -v: 丰富信息模式, 输出更详细的用例执行信息 -s: 显示程序中的print/logging输出 --resultlog=./log.txt 生成log --junitxml=./log.xml 生成xml报告 ''' pytest.main(['-s'])TestPy类
执行结果:
拓展
单元测试
定义:是指检查代码的实现和逻辑,针对的是模块、函数、类等
核心概念:
test case(测试用例):一个testcase的实例就是一个测试用例。测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)
TestSuite(测试套件):多个测试用例集合在一起。
TestLoader(测试运行器):用来加载Testcase到TestSuite中。
test fixture(测试环境数据准备和数据清理或者测试脚手架):测试用例环境的搭建和销毁
TextTestRunner:用来执行测试用例,其中run(test)会执行TestSuite/TestCase中的run(result)方法。
测试目录结构
Log:日志记录和管理功能,针对不同的情况,设置不同的日志级别,方便定位问题;
Report:测试报告生成和管理以及即时通知,测试结果快速响应;
Source:配置文件、静态资源的管理,遵循高内聚低耦合原则;
Common:公共函数、方法以及通用操作的管理,遵循高内聚低耦合原则;
TestCase:测试用例管理功能,一个功能点对应一个或者多个case,尽可能的提高覆盖率;
TestData:测试数据管理功能,数据与脚本分离,降低维护成本,提高可移植性;
TestSuite:测试组件管理功能,针对不同场景不同需求,组装构建不同的测试框架,遵循框架的灵活性和扩展性;
Statistics:测试结果统计管理功能,每次执行测试的结果统计、分析、对比以及反馈,数据驱动,为软件优化和流程改进,提供参考;
Continuous:持续集成环境,即CI环境,包括测试文件提交、扫描编译、执行测试、生成报告及时通知等功能,持续集成是自动化测试的核心!