Unittest框架
单元测试概述
定义:单元测试是指开发者写的一小段代码,再为集成到一起的时候。进行的测试
什么时间段测试:极限测试,越早越好,因为越早介入发现问题的就越早;边开发边测试
**谁发起:**一般多是开发发起
**单元测试规则:**案例中的预期结果要根据实际开发的逻辑与需求来写;不能按照开发实现的预期进行设计案例
单元测试目前现状
- Unittest:python的标准库,和java的Unit类似(常用于单元测试)
- Pytest:第三方库,更加灵活,可议结合allure生成测试报告
- Nose:是对Unittest的扩展
- Mock:标准库,用俩测试python的库
单元测试覆盖率
代码覆盖率用来度量测试是否全面的指标之一,应用覆盖率的思想增强测试用例的设计
单元测试覆盖类型
- 语句覆盖
- 条件覆盖
- 判断覆盖
- 路径覆盖
def demo_method(a,b,x):
if (a>1 and b==0):
x=x/a
if (a==2 or x>1):
x=x+1
return x
语句覆盖
- 语句覆盖定义
- 通过设计一定量的测试用例,保证测试方法每一行代码都会被执行一遍
- 运行测试用例的时候被击中的代码行即称为被覆盖的语句
- 测试用例
- 仅需要一条case,即可实现覆盖
- a=3,b=0,x=3
- 漏洞
- and ->or
- 行覆盖是一个最基础的覆盖方式,但也是最薄弱的,如果完全依赖行覆盖,会出现很严重的问题
判断覆盖
- 判断覆盖定义
- 运行测试用例的过程被击中的判定语句
- 测试用例
Test Case a b c (a>1)&&(b==0) a==2or x>1 ExcutaPath Case1 2 0 3 T T 135 Case2 1 0 1 F F 124 Case3 3 0 3 T F 134 Case4 231 F T 125
- 漏洞
- 大部分的判定语句是由多个逻辑条件组成的,若仅仅判断其整个最终结果,而忽略每个条件的取值情况,必然会遗漏部分测试路径
- a2 or x>1 -> a2 or x<1
t条件覆盖:
- 定义
- 条件覆盖和判定覆盖类似,不过判定覆盖关注整个判定语句,而条件覆盖则关注某个判断条件
- 测试用例: if (a<1 and b==0)
TestCase a>1 b==0 Case1 T F Case2 T F Case3 F T Case4 F F
- 缺陷
- 测试用例指数级增加(2**conditio0ns)
路径覆盖
定义
- 覆盖所有可能执行的路经
测试用例
TestCase a b x ExcultPath Case1 2 0 3 1 3 5 Case2 1 0 1 1 2 4 Case3 3 0 3 1 3 4 Case4 2 1 1 1 2 5 应用这些方法设计测试用例
unittest框架介绍
- python自带的单元测试框架,常用在单元测试
- 在自动化的测试中提供用例组织与执行
- 提供丰富的的断言方法-验证函数等功能
- 加上HTMLTestRunner可以生成html的报告
Unittest编写与规范
- unittest提供了testcase、testsuites、test fixtures、test runner的相关组件
- 编写规范
- 测试模块首先inport unittest
- 测试类必须继承unittest.TestCase
- 测试方法必须以"test_"开头
- 模块名字,类名字没有特殊要求
测试框架结构
- 总结:
- setup用来为测试准备环境,teardown用来清理环境
- 如果想要在所有case执行之前准备一次环境,并非所有case执行结束之后再清理环境,我们可以用setupclass()与teardown();比如数据库连接及销毁
- 如果有些方法不在本次执行使用@unittest.skip
- 测试方法的命名以test开头
- 各种执行-单一用力,全部
unittest实战
###案例一
import unittest class demo(unittest.TestCase): def setUp(self) -> None: print("setup") def tearDown(self) -> None: print("teardown") def test_case01(self): print("testcase01") self.assertEqual(1, 2, "判断相等") if __name__ == '__main__': unittest.main()
执行后代码
案例二
import unittest
class demo(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("setupclass")
@classmethod
def tearDownClass(cls) -> None:
print("teardowmclass")
def setUp(self) -> None:
print("setup")
def tearDown(self) -> None:
print("teardown")
def test_case01(self):
print("testcase01")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
def test_case02(self):
print("testcase02")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
def test_case03(self):
print("testcase03")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
if __name__ == '__main__':
unittest.main()
执行后代码
案例三
import unittest
class demo(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("setupclass")
@classmethod
def tearDownClass(cls) -> None:
print("teardowmclass")
def setUp(self) -> None:
print("setup")
def tearDown(self) -> None:
print("teardown")
def test_case01(self):
print("testcase01")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
def test_case02(self):
print("testcase02")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
@unittest.skipIf(1 + 1 == 2, "跳过这个版本")
def test_case03(self):
print("testcase03")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
if __name__ == '__main__':
unittest.main()
执行后代码
Unittest断言
Unittest执行测试用例
- 多个测试用例的集合就是测试套件,通过测试套件来管理多个测试用例
- 执行方法一:
- unittest.main()
- 执行方法二:加入容器中执行
- suite=unittest.TestSuite()
- suite.addTest(TestMethod(“test_01”))
- suite.addTest(TestMethod(“test_02”))
- unittest.TextTestRunner().run(suite)
- 执行方法三:此用法可以同时测试多个类
- suite1=unittest.TestLoader().loadTestsFromTestCase(TestCase1)
- suite1=unittest.TestLoader().loadTestsFromTestCase(TestCase2)
- suite=unittest.TestSuite([suite1,suite2])
- unittest.TextTestRunner(verbosity=2).run(suite)
- 执行方法四:匹配某个目录下所有以test开头的py文件,执行这些文件下的所有测试用例
- test_dir=“./test_case”
- discover=unittest.defaultTestLoader.discover(test_dir,pattern=“test*.py”)
- discover 可以一次调用多个脚本
- test_dir 被测试脚本的路径
- pattern 脚本名称匹配规则
测试用例执行过程
测试报告
- unittest结合htmltestrunner生成带日志的测试报告
import unittest
# from HTMLTestRunner.HTMLTestRunner import HTMLTestRunner
from HTMLTestRunner.HTMLTestRunner import HTMLTestRunner
class demo(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("setupclass")
@classmethod
def tearDownClass(cls) -> None:
print("teardowmclass")
def setUp(self) -> None:
print("setup")
def tearDown(self) -> None:
print("teardown")
def test_case01(self):
print("testcase01")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
def test_case02(self):
print("testcase02")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
@unittest.skipIf(1 + 1 == 2, "跳过这个版本")
def test_case03(self):
print("testcase03")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
class demo1(unittest.TestCase):
def test_01(self):
print("test01")
def test_02(self):
print("test02")
if __name__ == '__main__':
# 方法一
# unittest.main()
# 方法二
# suite = unittest.TestSuite()
# suite.addTest(demo("test_case02"))
# suite.addTest(demo1("test_02"))
# unittest.TextTestRunner().run(suite)
# # 方法三
# suite = unittest.TestLoader().loadTestsFromTestCase(demo)
# suite1 = unittest.TestLoader().loadTestsFromTestCase(demo1)
# suite2 = unittest.TestSuite([suite, suite1])
# unittest.TextTestRunner().run(suite2)
# # 方法四
# discover = unittest.defaultTestLoader.discover(".", "test*.py")
# unittest.TextTestRunner().run(discover)
# 方法五 ,encoding="utf-8"
suiteall = unittest.TestSuite()
suiteall.addTest(unittest.TestLoader().loadTestsFromTestCase(demo))
suiteall.addTest(unittest.TestLoader().loadTestsFromTestCase(demo1))
report_title = "Example用例执行报告"
desc = "用于展示修改样式后的HTMLTestRunner"
report_file = "./Examplereport.html"
with open(report_file, "w",encoding="utf-8") as report:
runner = HTMLTestRunner(stream=report, title=report_title
, description=desc)
runner.run(suiteall)
实战一
import unittest
class demo(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("setupclass")
@classmethod
def tearDownClass(cls) -> None:
print("teardowmclass")
def setUp(self) -> None:
print("setup")
def tearDown(self) -> None:
print("teardown")
def test_case01(self):
print("testcase01")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
def test_case02(self):
print("testcase02")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
@unittest.skipIf(1 + 1 == 2, "跳过这个版本")
def test_case03(self):
print("testcase03")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
class demo1(unittest.TestCase):
def test_01(self):
print("test01")
def test_02(self):
print("test02")
if __name__ == '__main__':
# 方法一
# unittest.main()
# 方法二
suite=unittest.TestSuite()
suite.addTest(demo("test_case02"))
suite.addTest(demo1("test_02"))
unittest.TextTestRunner().run(suite)
执行后代码
实战二
import unittest
class demo(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("setupclass")
@classmethod
def tearDownClass(cls) -> None:
print("teardowmclass")
def setUp(self) -> None:
print("setup")
def tearDown(self) -> None:
print("teardown")
def test_case01(self):
print("testcase01")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
def test_case02(self):
print("testcase02")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
@unittest.skipIf(1 + 1 == 2, "跳过这个版本")
def test_case03(self):
print("testcase03")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
class demo1(unittest.TestCase):
def test_01(self):
print("test01")
def test_02(self):
print("test02")
if __name__ == '__main__':
# 方法一
# unittest.main()
# 方法二
# suite = unittest.TestSuite()
# suite.addTest(demo("test_case02"))
# suite.addTest(demo1("test_02"))
# unittest.TextTestRunner().run(suite)
# 方法三
suite = unittest.TestLoader().loadTestsFromTestCase(demo)
suite1 = unittest.TestLoader().loadTestsFromTestCase(demo1)
suite2 = unittest.TestSuite([suite, suite1])
unittest.TextTestRunner().run(suite2)
执行后代码
实战三
import unittest
class demo(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("setupclass")
@classmethod
def tearDownClass(cls) -> None:
print("teardowmclass")
def setUp(self) -> None:
print("setup")
def tearDown(self) -> None:
print("teardown")
def test_case01(self):
print("testcase01")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
def test_case02(self):
print("testcase02")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
@unittest.skipIf(1 + 1 == 2, "跳过这个版本")
def test_case03(self):
print("testcase03")
# self.assertEqual(1, 2, "判断相等")
self.assertIn("h", "this", "判断是否包含")
class demo1(unittest.TestCase):
def test_01(self):
print("test01")
def test_02(self):
print("test02")
if __name__ == '__main__':
# 方法一
# unittest.main()
# 方法二
# suite = unittest.TestSuite()
# suite.addTest(demo("test_case02"))
# suite.addTest(demo1("test_02"))
# unittest.TextTestRunner().run(suite)
# # 方法三
# suite = unittest.TestLoader().loadTestsFromTestCase(demo)
# suite1 = unittest.TestLoader().loadTestsFromTestCase(demo1)
# suite2 = unittest.TestSuite([suite, suite1])
# unittest.TextTestRunner().run(suite2)
# 方法四
discover=unittest.defaultTestLoader.discover(".","test*.py")
unittest.TextTestRunner().run(discover)
执行后代码
te()
# suite.addTest(demo(“test_case02”))
# suite.addTest(demo1(“test_02”))
# unittest.TextTestRunner().run(suite)
# # 方法三
# suite = unittest.TestLoader().loadTestsFromTestCase(demo)
# suite1 = unittest.TestLoader().loadTestsFromTestCase(demo1)
# suite2 = unittest.TestSuite([suite, suite1])
# unittest.TextTestRunner().run(suite2)
# 方法四
discover=unittest.defaultTestLoader.discover(“.”,“test*.py”)
unittest.TextTestRunner().run(discover)
### 执行后代码
[外链图片转存中...(img-7LmCyZRF-1725267749916)]