测试:在规定的条件下对软件进行操作,发现软件存在的逻辑,功能,性能等问题
测试过程:单元测试–集成测试–系统测试–性能测试
单元测试:对软件设计最小单元进行正确性检测的测试,发现可能存在的问题。
单元测试的目的:就是发现模块内部的逻辑,语法,算法等错误。
单元测试方式:代码级别测试和模块功能测试。
代码级别测试:
接口测试:确保模块接口实现正确,符合设计文档规范或者约定。
数据结构测试:确保数据结构可用,数据库、文件、自定义数据结构;
边界测试:对于边界值测试,看模块会不会出现问题;
模块功能测试:通过黑盒,对模块测试。
其他单元测试项:性能,代码规范
框架unittest
介绍:三个步骤
找到测试类(testcase
),加载测试方法(runtest
),执行测试方法。
1、被测模块:model.py
// 定义一个类,提供接口,实现两数相加
'''
实现两个数的加减
'''
class Calculator():
def __init__(self,a,b):
self.a = int(a)
self.b = int(b)
'''加法'''
def add(self):
return self.a + self.b
'''减法'''
def sub(self):
return self.a - self.b
'''乘法'''
def mul(self):
return self.a * self.b
'''除法'''
def div(self):
return self.a / self.b
2、准备测试环境,编写测试用例
import unittest
from module import Calculator
from HTMLTestRunner2 import HTMLTestRunner
"""
1、我们添加了 setUp() 和 tearDown() 两个方法(其实是重写了TestCase的这两个方法),
这两个方法在每个测试方法执行前以及执行后执行一次,
setUp用来为测试准备环境,tearDown用来清理环境,已备之后的测试
2、@unittest.skip 跳过某个case不执行
"""
class ModuleTest(unittest.TestCase): //测试用例
def setUp(self): # 初始化,给被测模块的类传入参数
self.cal = Calculator(8,4)
def tearDown(self):
pass
// 调用方法,执行断言判断
def test_add(self):
result = self.cal.add()
self.assertEqual(result,12)
def test_sub(self):
result = self.cal.sub()
self.assertEqual(result, 4)
def test_mul(self):
result = self.cal.mul()
self.assertEqual(result, 32)
def test_div(self):
result = self.cal.div()
self.assertEqual(result, 2)
#if __name__ == "__main__":
# unittest.main()
if __name__ == "__main__":
#完全可以使用“unittest.main()”替代(前提是:所有的测试模块均以test开头)
suite = unittest.TestSuite() #第二步,构造测试集,加载需要用到的测试用例
suite.addTest(ModuleTest("test_add"))
suite.addTest(ModuleTest("test_sub"))
suite.addTest(ModuleTest("test_mul"))
suite.addTest(ModuleTest("test_div"))
#执行
#runner = unittest.TextTestRunner()
# runner.run(suite)
with open('HTMLReport.html','w') as f:
runner = HTMLTestRunner(stream=f, title='MathFunc Test Report',
description='generated by HTMLTestRunner.',
verbosity=2)
runner.run(suite) //第三步,调用执行
#若不保存测试输出结果,执行如下命令
#result = unittest.TextTestRunner(verbosity = 2).run(suite)
3、自动构成测试集
suite = unittest.makeSuite(测试用例名称,prefix = 'test')
(加载test
开头的文件)
import pytest
import unittest
from module import Calculator
from HTMLTestRunner2 import HTMLTestRunner
class ModuleTest(unittest.TestCase):
def setUp(self):
self.cal = Calculator(8,4)
def tearDown(self):
pass
def test_add(self):
result = self.cal.add()
self.assertEqual(result,12)
def test_sub(self):
result = self.cal.sub()
self.assertEqual(result, 4)
def test_mul(self):
result = self.cal.mul()
self.assertEqual(result, 32)
def test_div(self):
result = self.cal.div()
self.assertEqual(result, 2)
#如果在上面继续添加测试用例,为提高效率可以可以自动添加到测试集,如果不想把某些测试用例加载到测试集,**就不要用test开头**
if __name__ == "__main__":
suite = unittest.makeSuite(ModuleTest,prefix='test')
#suite.addTest(ModuleTest("get_number")) 同样可以结合这个addtest使用
print(suite.counTestCase()) #打印加载测试用例个数。
runner = unittest.TextTestRunner()
runner.run(suite)
测试用例的判断:也可以集成这些判断,进行扩展。
扩展 1、跳过某些接口,运行测试用例:
————-重写测试用例后,添加测试用例时,如何跳过某些用例,并给出出原因。
1、@unittest.skip('reason')
用例前加上装饰器,表明直接跳过。
@unittest.skip('notest')
def test_add(self):
result = self.cal.add()
self.assertEqual(result,12)
扩展2、根据某些特定的条件跳过一些测试用例
- skipIf(conditon,reason) // conditon 条件为真的话,就跳过并给出原因 reason
- skipUnless(condition,reason) //conditon 条件为真,就不跳过。
在测试模块中可以给出测试版本,选择版本跳过(测试时用例是一样编写的,可以用版本号选择跳过):
class Calculator():
version = 1 //版本一
def __init__(self,a,b):
self.a = int(a)
self.b = int(b)
'''加法'''
def add(self):
return self.a + self.b
'''减法'''
def sub(self):
return self.a - self.b
'''乘法'''
def mul(self):
return self.a * self.b
'''除法'''
def div(self):
return self.a / self.b
测试用编写时,可以使用选择跳过装饰器
@unittest.skipIf(Calculator.version == 1 ,'no test')
def test_add(self):
result = self.cal.add()
self.assertEqual(result,12)
扩展 3、预期结果失败 @exoextdFailure(func)
————-测试结果和预期结果不符合,不计入失败统计,断言的时有些结果不符合预期会发生执行错误,这个是否可以忽略不计入统计值
@unittest.expextdFailure
def test_add(self):
result = self.cal.add()
self.assertEqual(result,13) //显然这里不等于13
扩展 4、Mock
模拟测试
单元测试基本测试流程图:
( 设计测试用例— > 去测试基本模块里面对象,函数 —> 用到测试资源—>网络资源,第三方模块支撑)
存在的问题:单元测试脚本可能依赖于服务器搭建,第三方模块,以及测试结果难以知道,难以重复测试bug
解决 unittest.mock 模块: Mock
模块像测试对象提供一套和测试资源完全相同的接口和方法,不关心具体实现,只关心具体结果。第三方模块还没有完成时,可以用mock
去模拟第三方模块,给出接口值。
1、 mock.Mock
类 : 构造器,可以模拟其他类,增加属性
from unittest import mock
testmock = mock.Mock() //创建一个mock实例
print(testmock)
print(dir(testmock))
1、1 mock
提供的基本对象:
['assert_any_call', 'assert_called', 'assert_called_once',
'assert_called_once_with', 'assert_called_with', 'assert_has_calls',
'assert_not_called', 'attach_mock', 'call_args', 'call_args_list',
'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value',
'side_effect']
2、创建mock
类,构造其他属性。
from unittest import mock
attrs = ['connect','disconnect'] #增加两个属性
testmock = mock.Mock(name='TestMock',spec=attrs) # 创建一个mock对象,对象名字,对象属性
#testmock.connect.return_value = 200 #增加属性返回值
testmock.disconnect.return_value = 200
testmock.connect.side_effect = [200,404,501] #定义三个返回值,也可以定义一个返回函数,调用一次就返回一次。
for i in range(3):
print(testmock.connect())
3、Mock
断言语句
3、1 假设测试时用到的,第三方接口模块
第三方模块可能还没有完成,输入输出都不够完整,此时使用mock
改写无需关心第三方
"""
用 mock 模拟一个类,定义一个云端客户端,有四个基本的方法。
"""
from unittest import mock
class CouldClient(object):
def connect(self):
pass
def disconnect(self):
pass
def upload(self):
pass
def download(self):
pass
3、2 测试用例编写
import unittest
from unittest import mock
from mock_class import CouldClient
class TestCould(unittest.TestCase):
def setUp(self):
self.obj = mock.Mock(CouldClient) #构造 mock 实例传入改写测试类
def tearDown(self):
self.obj = None
# 改写并赋予测试类输出值,断言输出是否正确。
def test_connect(self):
self.obj.connect.return_value = 200
self.assertEqual(self.obj.connect(), 200)
if __name__ == '__main__':
unittest.main()