![171f7486cdefab49048c4957bb2fa13e.png](https://i-blog.csdnimg.cn/blog_migrate/09fcd99a3fbd11cee2ac55bb9c3071f1.png)
测试驱动开发(TDD)流程和我们平时修改代码流程是一样的:
写代码->print找bug->修改->print找bug
TDD的流程:
![4383318fcdf011614cd5560db74eb5e2.png](https://i-blog.csdnimg.cn/blog_migrate/9a8e752c9fba77d852a4efeba39c85b0.png)
TDD三项法则:
- 在编写失败的单元测试之前,不要编写任何产品代码
- 只要有一个单元测试失败了,就不再写测试代码;
- 产品代码能够让当前失败的单元测试成功通过即可,不要多写
python 测试库 unittest
,内置库,简洁易用。
直接举例子学习,如果测试下面的功能函数,将功能函数文件存储成mathfunc.py
def add(a, b):
return a+b
def minus(a, b):
return a-b
def multi(a, b):
return a*b
def divide(a, b):
return a/b
TestCase测试用例
通过测试用例对上面功能进行测试
import unittest
from mathfunc import add, minus, multi, divide
# 一个class继承了unittest.TestCase,便是一个测试用例
class TestMathFunc(unittest.TestCase):
# TestCase基类方法,所有case执行之前自动执行
@classmethod
def setUpClass(cls) -> None: # 1
print("这里是所有测试用例前的准备工作")
# TestCase基类方法,所有case执行之后自动执行
@classmethod
def tearDownClass(cls) -> None: # 2
print("这里是所有测试用例后的清理工作")
# TestCase基类方法,每次执行case前自动执行
def setUp(self) -> None: # 3
print("这里是一个测试用例前的准备工作")
# TestCase基类方法,每次执行case后自动执行
def tearDown(self) -> None: # 4
print("这里是一个测试用例后的准备工作")
@unittest.skip("想临时跳过这个测试用例") # 5
def test_add(self):
print("测试加法")
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(2, 2)) # 测试业务方法add
def test_minus(self):
print("测试减法")
self.skipTest("跳过这个测试用例") # 6
self.assertEqual(q, minus(3, 2)) # 测试业务方法multi
def test_multi(self):
print("测试乘法")
self.assertEqual(6, multi(2, 3)) # 测试业务方法multi
def test_divide(self):
print("测试除法")
self.assertEqual(2, divide(6, 3)) # 测试业务方法divide
self.assertEqual(2.5, divide(5, 2))
if __name__ == '__main__':
unittest.main(verbosity=2)
注意:
- 一个class继承了unittest.TestCase,便是一个测试用例
- 在每一个测试用例中在代码中
1, 2, 3, 4
位置,重写以下函数:setUp()
该测试用例执行前的设置工作、tearDown()
该测试用例执行后的清理工作、setUpClass()
所有测试用例前的设置工作、tearDownClass()
所有测试用例执行后的清洗工作
- 每一个测试用例中可以通过skip,skipIf,skipUnless装饰器跳过某个测试函数,或者用TestCase.skipTest方法跳过测试函数,代码
5, 6
所示 - 每个测试方法均以 test 开头,否则是不被unittest识别的。每一个test开头的方法都会加载为独立的测试用例。执行顺序按函数名字典排序顺序依次执行
- 在unittest.main()中加 verbosity 参数可以控制输出的错误报告的详细程度,默认是 1,如果设为 0,则不输出每一用例的执行结果。如果参数为2则表示输出详细结果
TestSuite测试组
TestSuite用来控制多个测试用例和多个测试文件之间的测试顺序。(默认顺序是按函数名字典排序顺序依次执行)
import unittest
from tests.test_mathfunc import TestMathFunc
from tests.HTMLTestRunner import HTMLTestRunner
if __name__ == '__main__':
suite = unittest.TestSuite()
tests = [TestMathFunc("test_multi"), TestMathFunc("test_divide")] # 添加测试用例列表
# 添加一组测试用例
suite.addTests(tests)
# 添加一个测试用例
suite.addTest(TestMathFunc("test_add"))
# loadTestsFromName(),传入'模块名.TestCase名'
# 传入单个
suite.addTests(unittest.TestLoader().loadTestsFromName("test_mathfunc.TestMathFunc"))
# 传入一个列表
suite.addTests(unittest.TestLoader().loadTestsFromNames(["test_mathfunc.TestMathFunc"]))
# loadTestsFromTestCase(),传入TestCase
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))
#
# suite中也可以套suite
# 将测试结果输出到测试报告中
# with open('UnittestTextReport.txt', 'w', encoding="utf-8") as f:
# runner = unittest.TextTestRunner(stream=f, verbosity=2)
# runner.run(suite)
# 将测试结果输出到测试报告html中
# with open('HTMLReport.html', 'w') as f:
# runner = HTMLTestRunner(stream=f,
# title='MathFunc Test Report',
# description='generated by HTMLTestRunner.',
# verbosity=2
# )
# runner.run(suite)
# 直接将结果输出控制台
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
注意:
- 多种方法将多个测试用例打包,一起测试
- 测试报告,可以打印
txt
,html
和输出控制台。其中html的输出需要加载HTMLTestRunner.py模块
unittest ddt数据驱动
当我们想对一个功能使用多次测试数据时候,这时候使用DDT
来完成,DDT是 “Data-Driven Tests”的缩写。
dd.ddt:
装饰类,也就是继承自TestCase的类。
ddt.data:
装饰测试方法。参数是一系列的值。
ddt.file_data:
装饰测试方法。参数是文件名。文件可以是json
或者 yaml
类型。
注意,如果文件以”.yml”
或者”.yaml”
结尾,ddt会作为yaml
类型处理,其他所有文件都会作为json
文件处理。
如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。
如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。
ddt.unpack:
传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多个参数上。
直接举个用例子说明:
from mathfunc import add
import unittest
from ddt import ddt, data, unpack
@ddt # 1
class TestMathFunc(unittest.TestCase):
@data([1, 2, 3], [2, 2, 3]) # 2 测试两组数据
@unpack # 3 相当于把第一组数据[1, 2, 3]分别传参给num1, num2, correct
def test_add(self, num1, num2, correct):
print("测试加法")
result = add(num1, num2)
self.assertEqual(correct, result) # 测试业务方法add
if __name__ == '__main__':
unittest.main(verbosity=2)
注意:
- 代码1处装饰类,也就是继承自TestCase的类。
- 代码2处采用@ddt进行装饰,测试方法上装饰@data(),data可以是数值,也可以是字符串。测试方法后会被ddt加一个后缀,ddt会尝试把测试数据转化为后缀附在测试方法后,组成一个新的名字。
data 可以传入列表,元组和字典都可以; - 代码3就是解包,分别传参了到相应参数。
我们还可以直接传入json
或者yaml
文件,这里我们只用json
举例子,新建一个文件test_data.json
[
{
"num1": 2,
"num2": 3,
"correct": 6
},
{
"num1": 2,
"num2": 3,
"correct": 5
}
]
file_data
的用法学习
import unittest
from ddt import ddt, file_data
from mathfunc import multi
@ddt # 1
class TestMathFunc(unittest.TestCase):
@file_data("test_data.json")
def test_multi(self, num1, num2, correct):
print("测试乘法")
result = multi(num1, num2)
self.assertEqual(correct, result) # 测试业务方法multi
参考链接:
- https://zhuanlan.zhihu.com/p/29968920
- https://blog.csdn.net/luanpeng825485697/article/details/79459771
- https://www.cnblogs.com/miniren/p/7099187.html