目录
为什么做单元测试
1.单元测试:对于软件设计最小的单元进行正确性测试,将可能存在的错误在最小范围内解决。
2.目的:发现模块内部逻辑,语法,算法,功能等错误。
3.测试方式:代码级别的测试和模块功能的测试
3.1代码级别测试
根据内部逻辑与接口编写测试用例
接口测试:确保接口实现正确,符合设计文档规范
数据结构测试:确保数据接口可用,例如数据库,文件,自定义数据结构;
边界测试:对于边界进行测试
单元测试用例编写
基础测试用例
def add(*args):
result = 0
for i in args:
result +=i
return result
print(add(1))
print(add(1,2))
print(add(1,"a"))
...
1
3
Traceback (most recent call last):
File "C:\Users\shitou\eclipse-workspace\python_test\src\test\__init__.py", line 9, in <module>
print(add(1,"a"))
File "C:\Users\shitou\eclipse-workspace\python_test\src\test\__init__.py", line 4, in add
result +=i
TypeError: unsupported operand type(s) for +=: 'int' and 'str'
unittest单元测试框架
1.测试用例:
1).测试环境准备
2).测试代码执行
3).测试环境还原
4).测试回归化
5).测试结果分析
2.测试用例结构
import unittest
class MyTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
#初始化测试:构建预置条件,套件执行一次
def setUp(self):
#初始化:构建预置条件,每个脚本执行一次
def tearDown(self):
#恢复测试环境:清理测试数据等,每个脚本执行一次
@classmethod
def tearDownClass(cls):
#恢复测试环境:清理测试数据等,套件执行一次
def test_xxx(self):
#测试用例必须以test开头
if __name__=="__main__":
unittest.main()#执行测试用例
3.实例测试
1)待测方法
def add(a,b):
return a+b
2)测试用例
import unittest
from test import myfunction as MF
class MyTest(unittest.TestCase):
def setUp(self):
print("test start")
def tearDown(self):
print("end test")
def test_001(self):
result = MF.add(1, 2)
self.assertEqual(result, 3)
def test_002(self):
result = MF.add(0, 2)
self.assertEqual(result, 2)
def test_003(self):
self.assertRaises(TypeError, MF.add(0, 2))
if __name__=="__main__":
unittest.main()
测试集unittest.TestSuite
import unittest
from test import myfunction as MF
class MyTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_001(self):
result = MF.add(1, 2)
self.assertEqual(result, 3)
def test_002(self):
result = MF.add(0, 2)
self.assertEqual(result, 2)
def test_003(self):
self.assertRaises(TypeError, MF.add(0, 2))
if __name__=="__main__":
suite = unittest.TestSuite()
#添加单个测试用例
suite.addTest(MyTest("test_001"))
suite.addTest(MyTest("test_002"))
#添加多个测试用例
test_cases = [MyTest("test_003")]
suite.addTests(test_cases)
#统计测试用例数
tc_num = suite.countTestCases()
print(tc_num) #3
runner = unittest.TextTestRunner(verbosity=0)
runner.run(suite) #执行
#0 (静默模式): 你只能获得总的测试用例数和总的结果 比如 总共100个 失败20 成功80
3
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
#1 (默认模式): 非常类似静默模式 只是在每个成功的用例前面有个“.” 每个失败的用例前面有个 “F”
3
.F.
#2 (详细模式):测试结果会显示每个测试用例的所有相关的信息
并且 你在命令行里加入不同的参数可以起到一样的效果
3
test_001 (__main__.MyTest) ... ok
test_002 (__main__.MyTest) ... ok
test_003 (__main__.MyTest) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
断言
result = MF.add(1, 2)
self.assertEqual(result, 4,"预期是%d,实际是%d")
self.assertTrue(expr, msg)
...
跳过测试用例
1.使用装饰器unittest.skip
import unittest
from test import myfunction as MF
class MyTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_001(self):
result = MF.add(1, 2)
self.assertEqual(result, 3,"预期是%d,实际是%d")
@unittest.skip("Not Test")
def test_002(self):
print("no test")
result = MF.add(0, 2)
self.assertEqual(result, 3)
#
# def test_003(self):
# self.assertRaises(TypeError, MF.add(0, 2))
#
if __name__=="__main__":
unittest.main()
# .
# ----------------------------------------------------------------------
# Ran 2 tests in 0.000s
#
# OK (skipped=1)
2.skipIf
测试方法
version = 1
def add(a,b):
return a+b
测试用例
import unittest
from test import myfunction as MF
class MyTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_001(self):
result = MF.add(1, 2)
self.assertEqual(result, 3,"预期是%d,实际是%d")
@unittest.skipIf(MF.version==1, "跳过测试") #满足条件则不执行
def test_002(self):
result = MF.add(0, 2)
self.assertEqual(result, 3)
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK (skipped=1)
if __name__=="__main__":
unittest.main()
3.skipUnless
import unittest
from test import myfunction as MF
class MyTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_001(self):
result = MF.add(1, 2)
self.assertEqual(result, 3,"预期是%d,实际是%d")
@unittest.skipIf(MF.version==1, "跳过测试") #满足条件则跳过
def test_002(self):
result = MF.add(0, 2)
self.assertEqual(result, 3)
@unittest.skipUnless(MF.version==1, "跳过测试") #不满足条件则跳过
def test_003(self):
result = MF.add(0, 3)
self.assertEqual(result, 3)
if __name__=="__main__":
unittest.main()
4.expectedFailure
import unittest
from test import myfunction as MF
class MyTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_001(self):
result = MF.add(1, 2)
self.assertEqual(result, 3,"预期是%d,实际是%d")
@unittest.expectedFailure
def test_002(self):
result = MF.add(0, 2)
self.assertEqual(result, 3)
if __name__=="__main__":
unittest.main()
扫描所有测试用例
unittest.defaultTestLoader.discover
def set_test_suite(self):
"""
set case suite
:return:
"""
#定义测试套件
#定义测试文件查找的目录
test_suite = unittest.TestSuite()
testcase_dir = os.path.join(RC.proDir,"testcase")
discover = unittest.defaultTestLoader.discover(testcase_dir, pattern = 'TS*.py', top_level_dir=None)
#扫描所有TS开头的py文件,然后把测试用例添加到测试集合
if discover:
for suite in discover:
for test_case in suite:
test_suite.addTest(test_case)
else:
return None
return test_suite
生成测试报告HTMLTestRunner
def runAllTest(self):
"""
run test
:return:
"""
try:
suite = self.set_test_suite()
if suite:
logger.info("********TEST START********")
fp = open(reportPath, 'wb')
runner = HT.HTMLTestRunner(stream=fp,
verbosity=2,
title='Test Report',
description='Test Description')
runner.run(suite)
fp.close()
time.sleep(3)
else:
logger.info("Have no case to test.")
except Exception as ex:
补充
1.unittest.main()执行过程
收集测试用例
根据case生成测试集
运行测试用例