前言
unittest原名为PyUnit,是由java的JUnit衍生而来。unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。unittest作为官方的测试框架,在测试方面非常基础,可以在此基础上进行二次开发。
unittest工作原理
unittest结构图:
- test case :一个完整的测试单元,执行该测试单元可以完成对某一个问题的验证,完整体现在:测试前环境准备(setUp),执行测试代码(run),及测试后环境还原(tearDown);
- test suite :多个测试用例的集合,测试套件或测试计划;
- testLoader :加载TestCase到TestSuite中的,其中loadTestsFrom__()方法用于寻找TestCase,并创建它们的实例,然后添加到TestSuite中,返回TestSuite实例;
- test runner :执行测试用例,并将测试结果保存到TextTestResult实例中,包括运行了多少测试用例, 成功了多少,失败了多少等信息;
- test fixture:一个测试用例的初始化准备及环境还原,主要是setUp() 和 setDown()方法;
unittest简单示例
先通过一个简单的示例,对unittest有一个直观的了解:
import unittest
class Mytest1(unittest.TestCase):
def setUp(self) -> None:
print("===== setUp =====")
def tearDown(self) -> None:
print("===== tearDown =====")
@classmethod
def setUpClass(cls) -> None:
print("===== setUpClass =====")
@classmethod
def tearDownClass(cls) -> None:
print("===== tearDownClass =====")
def test_add(self):
print("测试函数add")
self.name = "Aaa"
def test_down(self):
print("测试函数down")
self.name = "Bbb"
# 文件内执行方式
if __name__ == "__main__":
unittest.main()
# 命令行执行,scratch_11.py为包含
python -m unittest scratch_11.py
执行结果:
===== setUpClass =====
===== setUp =====
测试函数add
===== tearDown =====
===== setUp =====
测试函数down
===== tearDown =====
===== tearDownClass =====
通过上面的例子,我们可以总结:
- setUpClass:整个测试开始后执行,只执行一次
- tearDownClass:整个测试完成后执行,只执行一次
- setUp:每运行一次用例前都会执行一次
- tearDown:每运行一次用例后都会执行一次
- 测试用例的命名规则为test_xxx,不以test_xxx命名的函数是方法,方法是不能被执行的
断言方法
测试中,我们会经常用到判断某个函数的返回结果是否等于某个值或者等于某个类型这种情况,非常适合使用断言来判断。在UnitTest中,TestCase 已经提供有封装好的断言方法进行断言校验。断言强调的是对于整个测试流程的结果进行判断,所以断言的内容是极为核心的。
- assertEqual(a,b) a==b 值是否相等
- aassertNotEqual(a,b) a!=b 值是否不相等
- aasserIs(a,b) a is b 值是否相同
- aassertIsNot(a,b) a is not b 值是否不同
- assertIn(a,b) a in b a是否包含b
- assertNotIn(a,b) a not in b a是否不包含b
- ssertTrue(a) bool(a) is true 是否为真
- assertFalse(a) bool(a)is false 是否为假
- assertIsNone(a) a is None 是否为空
- assertIsNotNone(a) a is None 是否不为空
- assertIsInstance(a,b) Instance(a,b) a与b的数据类型一样
- assertNotIsInstance(a) not Instance(a,b) a与b的数据类型不一样
unittest各种用法
UnitTest.skip
有时候,测试用例很多,有些需要执行,有些不想执行,那就需要跳过一些测试用例,这时候就需要用到skip,skip用法:
- 无条件跳过,unittest.skip(“xxx”)
- 条件为True跳过,unittest.skipIf(1 < 2, ‘xxx’)
- 条件为False跳过,unittest.skipUnless(1 > 2, ‘xxx’)
- 执行失败不计入case总数中,unittest.expectedFailure
示例如下:
import unittest
class Mytest1(unittest.TestCase):
def setUp(self) -> None:
print("===== setUp =====")
def tearDown(self) -> None:
print("===== tearDown =====")
@classmethod
def setUpClass(cls) -> None:
print("===== setUpClass =====")
@classmethod
def tearDownClass(cls) -> None:
print("===== tearDownClass =====")
@unittest.skip("直接跳过")
def test_1(self):
print("test_1")
@unittest.skipIf(1<2,"条件为True则跳过")
def test_2(self):
print("test_2")
@unittest.skipUnless(1<2,"条件为False则跳过")
def test_3(self):
print("test_3")
@unittest.expectedFailure
def test_4(self):
print("test_4")
self.assertEqual(4,5)
if __name__ == "__main__":
unittest.main()
执行结果如下,可以看到跳过了2个,对应test_1、test_2,test_3不满足条件,没有跳过,test_4执行错误,但是没有计入错误总数中:
ss.x
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK (skipped=2, expected failures=1)
===== setUp =====
test_3
===== tearDown =====
===== setUp =====
test_4
===== tearDown =====
结果中ss.x代表的意思:
- . 表示通过 或者 pass
- S 表示跳过
- x 表示忽略错误
- F False, 表示断言没有通过
- E Error, 表示程序内部发生了错误
unittest.TestSuite
上面的TestCase例子中,4个测试方法的执行顺序是按照1 2 3 4的顺序,如果我们想自定义执行顺序怎么办,比如2可能依赖于1,在unittest中解决用例执行顺序的问题是使用TestSuite,测试套件TestSuite的作用:
- 用于给测试用例进行排序
- 管理测试用例
先看一个简单的示例:
from unittest import TestSuite
suite = TestSuite()
# 添加单个测试用例
suite.addTest(Mytest1("test_3"))
suite.addTest(Mytest1("test_1"))
suite.addTest(Mytest1("test_2"))
# 添加多个测试用例
cases = [Mytest1('test_3'), Mytest1('test_1'), Mytest1('test_2')]
suite.addTests(cases)
# 执行测试,unittest.TextTestRunner功能相当于unittest.main
runner = unittest.TextTestRunner()
runner.run(suite)
生成html格式测试报告
有时候我们需要更好的结果展示方式,可以使用HtmlTestRunner模块生成html格式的报告,安装后导入该模块,使用HTMLTestRunner代替默认的TextTestRunner()执行测试用例即可。
pip install html-testRunner
安装HtmlTestRunner模块。
示例如下:
from unittest import TestSuite
from HtmlTestRunner import HTMLTestRunner
class Mytest1(unittest.TestCase):
def test_1(self):
print("test_1")
def test_2(self):
print("test_2")
def test_3(self):
print("test_3")
def test_4(self):
print("test_4")
if __name__ == "__main__":
# 创建一个测试套件 suite
suite = TestSuite()
# 添加单个测试用例
suite.addTest(Mytest1("test_3"))
suite.addTest(Mytest1("test_1"))
suite.addTest(Mytest1("test_2"))
# 执行测试并输出html报告,output输出到xx目录下
runner = HTMLTestRunner(output="result")
runner.run(suite)
然后就可以在对应目录下查看执行的结果了:
ddt模块
有时我们需要对大量的数据进行同一种逻辑函数测试,这时候可以用到ddt模块。
ddt,是一种 数据驱动测试 思想,数据和用例进行分离,通过外部数据去生成测试用例,可以快速的对大量数据和不同情况进行测试。
首先我们需要在python中安装ddt模块:pip install ddt
。
数据引入的方式主要有以下几种:
- 通过@data装饰器的方式
- json文件
- yaml文件
- 其他
使用方法
照常先使用一个简单示例来演示:
使用data装饰器
import unittest
from ddt import ddt, data, unpack
# 先定义@ddt,用于表示要使用ddt了
@ddt
class Mytest1(unittest.TestCase):
# data用于设定参数
@data("a", "b", "c")
def test_1(self, text):
self.assertEqual("a", text)
if __name__ == "__main__":
unittest.main()
使用json文件
先编写一个json文件:
[
{
"name": "test",
"age": 18
},
{
"name": "test1",
"age": 20
}
]
编写测试代码:
import unittest
from ddt import ddt, file_data
@ddt
class Mytest1(unittest.TestCase):
@file_data("/Users/tangliqi/Desktop/个人文档/json文件/test.json")
def test_1(self, **text):
print(text)
print(type(text))
if __name__ == "__main__":
unittest.main()
输出结果:
{'name': 'test', 'age': 18}
<class 'dict'>
{'name': 'test1', 'age': 20}
<class 'dict'>
使用yaml文件
编写一个yaml文件:
# "-"表示一个列表
-
name: 'test1'
age: 18
-
name: 'test2'
age: 20
编写测试代码:
import unittest
from ddt import ddt, file_data
@ddt
class Mytest1(unittest.TestCase):
@file_data("/Users/tangliqi/Desktop/个人文档/json文件/test.yaml")
def test_1(self, **text):
print(text)
print(type(text))
if __name__ == "__main__":
unittest.main()
执行结果:
{'name': 'test1', 'age': 18}
<class 'dict'>
{'name': 'test2', 'age': 20}
<class 'dict'>