单元测试概述
- 单元测试:用于判断某个特定条件(或者场景)下某个特定函数的行为
- 单元测试由谁负责:开发
- 单元测试开始时间:越早越好
- 注意事项:需要知道预期的输入输出,编写case,针对需求/设计的逻辑写case,而不是针对实现去写
单元测试框架
- Unittest:Python内置的标准库。它的API跟Java的JUint、.net的NUnit,C++的CppUnit很相似
- pytest:丰富、灵活的测试框架,语法简单,可以结合allure生成一个炫酷的测试报告,现在比较主流
- Nose:针对unittest的扩展,使得python的测试更加简单
- Mock:unittest.mock是用来测试python的库。这个是一个标准库(出现在python3.3版本以后)
- …
单元测试覆盖率
- 代码覆盖率也被用于自动化测试和手工测试来度量测试是否全面的指标之一,应用覆盖率的思想增强测试用例的设计
- 单元测试覆盖类型
- 语句覆盖
- 测试用例保证测试的方法每一行代码都会被执行
- 被覆盖的语句:被击中的代码行
- 测试用例:a = 3,b = 0,x = 3
- 漏洞:and > or
- 行覆盖是最基本的覆盖,最薄弱,完全依赖行,会出现严重问题
- 条件覆盖
- 概念:关注每个判断条件
- 用例:每个条件实现true和false
- 缺陷:测试用例指数级别增加(2**conditions)
- 判断覆盖
- 概念:运行测试用例过程被击中的判定语句
- 测试用例:每个判断条件分别满足true和false即可
- 漏洞:判断语句由多个条件组成,仅仅判断整个条件结果,忽略取值情况,可能会遗漏部分测试路径
- 漏洞用例:a=2 or x >1 ->a=2 or x<1
- 路径覆盖
- 概念:覆盖所有可能执行的路径
- 测试用例:满足路径写测试用例,可能需要画一下路径图
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
unittest框架介绍
- python自带框架,常用于单元测试
- 在自动化测试中提供用例组织于与执行
- 提供丰富的断言-验证函数等功能
- 加上HTMLTestRunner可以生成html的报告
- 现在依然有些公司再用这些框架
unittest编写与规范
- Unittest提供了test cases、test suites、test fixtures、test runner相关的组件
- 编写规范
- 测试模块首先import unittest
- 测试类必须继承unittest.TestCase
- 测试方法必须以"test_”开头
- 模块名字,类名没有特殊要求
unittest测试结构
- setUp用来为准备测试环境,tearDown用来清理环境
- setUpClass()与tearDownClass():在所有case前准备一次环境,并在所有case执行结束之后在清理环境,我们可以用setUpClass()与tearDownClass();比如数据库连接与销毁
- 跳过某个测试用例:@unittest.skip(“跳过理由”)
- 测试方法命名:以test开头
- 各种执行-单一用例,全部
import unittest
class TestClass(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("setUpclass")
def setUp(cls):
print("setUp")
@classmethod
def tearDownClass(cls) -> None:
print("tearDoenClass")
def tearDown(cls):
print("tearDown")
def test_case01(self):
print("testcase01")
self.assertEqual(1, 2, "判断相等")
self.assertIn('h', 'this')
@unittest.skipIf(1 + 1 == 2, "跳过吧")
def test_case02(self):
print("testcase02")
self.assertEqual(1, 2, "判断相等")
self.assertIn('h', 'this')
@unittest.skip("皮卡丘")
def test_case03(self):
print("testcase03")
self.assertEqual(1, 2, "判断相等")
self.assertIn('h', 'this')
if __name__ == '__main__':
unittest.main()
unittest断言
unittest执行测试用例
- 多个测试用例的集合就是测试套件,通过测试套件来管理多个测试用例
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(TestClass("test_case01"))
suite.addTest(TestClass2("test_case01"))
unittest.TextTestRunner().run(suite)
if __name__ == '__main__':
suite1 = unittest.TestLoader().loadTestsFromTestCase(TestClass)
suite2 = unittest.TestLoader().loadTestsFromTestCase(TestClass2)
suite = unittest.TestSuite([suite1,suite2])
unittest.TextTestRunner().run(suite)
test_dir = "./"
if __name__ == '__main__':
discover = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py")
unittest.TextTestRunner().run(discover)
测试用例执行过程
- 首先是写好测试用例
- 然后由TestLoader加载TestCase到TestSuite
- 然后由TextTestRunner来运行TestSuite
- 运行结果保存在TextTestResult中
- 整个过程集成在unittest.main模块中
- TestCase可以是多个,TestSuit也可以是多个
htmltestrunner生成带日志的测试报告
- http://tungwaiyip.info/software/HTMLTestRunner.html
- https://github.com/huilansame/HTMLTestRunner_PY3
- 可以直接查看download下来的测试案例
- 手动下载HTMLTestRunner.py文件,放在python目录Lib文件夹下即可
- 无法生成测试报告参考
if __name__ == '__main__':
report_title = 'Example用例执行报告'
desc = '用于展示修改样式后的HTMLTestRunner'
report_file = 'ExampleReport.html'
testsuite = unittest.TestSuite()
testsuite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestTest))
testsuite.addTest(unittest.TestLoader().loadTestsFromTestCase(ExampleCase1))
testsuite.addTest(unittest.TestLoader().loadTestsFromTestCase(ExampleCase2))
testsuite.addTest(unittest.TestLoader().loadTestsFromTestCase(ExampleCase3))
with open(report_file, 'wb') as report:
runner = HTMLTestRunner(stream=report, title=report_title, description=desc)
runner.run(testsuite)