功能测试
等价类划分法
通过科学的方法找到具有共同特性的测试输入的子集,能够从穷举测试中解放(大大减少了测试用例的数量,从而提升测试效率。)典型应用场景比如:输入框
- 设计测试用例,需求分析,划分等价类
- 有效
- 无效
- 规则(需求本身)
- 长度
- 类型
- 是否为空(必填项)
- 是否重复
边界值
对等价类的补充,统计表明程序最容易出错的地方就是在边界附近典型应用场景:存在边界,>,>=,<,<=
- 上点:边界之上的点
- 内点:边界之内的点
- 离点:离边界最近的左右两点
判定表
存在多个输入条件、多个输出结果,输入和输入之间有组合关系,输入和输出之间有依赖或制约关系
- 条件桩:所有输入条件,如欠费状态、关机状态
- 动作桩:所有的可能的输出结果,如打通、打不通
- 条件项:单个条件的取值范围,一般都是有效等价类和无效等价类
- 字符:
- 真/有效等价类/Y
- 假/无效等价类/N
- 数字:
- 真/有效等价类/1
- 假/无效等价类/0
- 动作项:基于每一种条件的组合,得到确认的结果,如打不通等
设计测试用例的步骤
- 明确条件桩(找到所有的输入条件)
- 明确动作桩(找到所有的输出结果)
- 对条件桩进行全组合
- 明确每个组合对应的动作项(基于每一种条件的组合情况,确定本组合下的输出结果。)
- 设计测试用例,每行数据对应一条测试用例
正交法
用最小的测试用例获得最大的测试覆盖率
- k代表因素(输入参数)
- m叫水平(输入参数的取值)
- n代表测试用例数
- 读法:k因素m水平
- 比如如图所示3因素2水平代表一共有3个参数每个参数都对应2个值,一共有8种结果,根据这张表进行4次试验,可以获得最大的覆盖率,可以根据实际的情况对照不同的正交表
基于正交表设计测试用例
- 需求分析
- 确定因素与水平(因素:控件名称;水平:每个控件对应的取值)
- 确定要采用的正交表
- 将正交表中的字母用文字代替
- 设计测试用例(一行就是一条测试用例)
场景法(流程图法)
场景法就是模拟用户操作软件时的场景,主要用于测试多个功能之间的组合使用情况
- 使用测试阶段
- 集成测试
- 系统测试
- 验收测试
- 设计测试用例的步骤
- 需求分析
- 绘制流程图
- 设计测试用例(一条流程路径就是一条测试用例)
- 流程图常用符号
- 开始或结束:椭圆
- 方向或路径:箭头
- 处理或操作:长方形
- 判断:菱形
- 输入或输出:平行四边形
- 绘制流程图
- 第1步:确认场景中关键业务步骤
- 第2步:确定业务之间的先后顺序
- 第3步:用箭头连接即可
测试用例设计方法总结
- 具有输入功能,但输入之间没有组合关系==》【等价类】
- 输入有边界 如长度、类型==》【边界值】
- 多输入、多输出、输入与输入之间存在组合关系、输入与输出之间存在依赖或制约关系==》【判定表】
- 用最少的测试用例获得最大测试覆盖率时 ==》【正交法】
- 多个功能的组合测试 ==> 【场景法、流程图】
- 最后推荐使用【错误推测法】来进一步补充测试用例
缺陷及缺陷管理
缺陷的定义
- 产品实现不满足用户需求
- 测试执行时,实际结果和预期结果不一致
缺陷的判定标准
- 未达到需求说明书指明的功能
- 出现了需求说明书指明不应该出现的错误
- 实现了需求说明书之外的功能
- 未达到需求说明书虽未明确提及但是应该实现的目标(如:性能要求等)
- 用户角度发现的各种问题与错误
缺陷产生的原因及根本原因
缺陷产生的原因
- 需求文档存在错误
- 需求变更
- 设计存在错误
- 代码错误
缺陷产生的根本原因
- 需求变更
- 沟通不畅、信息不同步
- 软件复杂
- 进度压力
软件缺陷的核心内容
- 标题:描述缺陷的基本信息,如(输入密码长度为5时,竟然注册成功。)
- 前置条件:描述缺陷出现依赖的相关基础条件,如(未注册用户名)
- 复现步骤:测试用例里面的执行步骤
- 实际结果:执行被测试软件过程中,系统给出的结果
- 预期结果:参照需求说明书,在测试用例中设计的预期结果
- 附件:方便开发定位bug的关键信息,包含图片、日志log等
缺陷基本要素
- ID编号:唯一
- 模块:根据产品进行具体的划分,如登录、注册
- 缺陷状态:表明缺陷处理进度
- 严重程度:从技术维度来衡量,bug的破坏力
- 优先级:从业务的角度,决定bug修改的先后顺序
- 缺陷类别:用于分类整理缺陷
缺陷的状态
- new:新建
- open:打开
- fix:已修复
- close:关闭
- reopen:重新打开
- reject:已拒绝
- postpone:延期
缺陷严重程度
- 5-致命的
- 4-非常高
- 3-高
- 2-中
- 1-低
缺陷优先级
- 5-紧急的
- 4-非常高
- 3-高
- 2-中
- 1-低
缺陷类别
- 功能错误
- UI界面错误
- 兼容性
- 易用性
- 改进建议
- 其他
缺陷书写规范
- 标题:应保持简短、准确,提供缺陷的本质信息
- 复现步骤:应包含如何使别人能够很容易的复现该缺陷的完整步骤
- 实际结果:是执行复现步骤后软件的现象和产生的行为
- 预期结果:通常需要列出期望的结果是什么
- 附件:对缺陷描述的补充说明
缺陷跟踪流程
- 场景1:确认BUG解决
- 测试【new】==》开发【open】==》开发【fix】==》测试【close】
- 场景2:验证未通过,缺陷仍存在
- 测试【new】==》开发【open】==》开发【fix】==》测试【reopen】
- 场景3:开发延期处理
- 测试【new】==》开发【open】==》开发【postpone】
- 场景4:拒绝处理
- 测试【new】==》开发【open】==》开发【reject】
UnitTest单元测试框架
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证,对于单元测试中单元的含义,要根据实际情况去判定其具体含义,一个单元可能是功能模块、类、方法(函数)等,不同的编程语言都有比较成熟的单元测试框架,语法规则有些差别,其核心思想都是相通的,常见的单元测试框架有:
- Java语言:Junit、TestNG
- Python语言:UnitTest、Pytest
UnitTest框架概念
UnitTest是Python自带的一个单元测试框架,用它来做单元测试。也经常应用到UI自动化测试和接口自动化测试中,用来管理和维护测试用例脚本
- 使用UnitTest框架的好处
- 能够组织多个用例去执行(可以把多条测试用例封装成一个测试套件,实现批量执行测试用例)
- 提供了丰富的断言方法,方便对用例执行的结果进行判断
- 能够生成HTML格式的测试报告
- 使用Fixture功能可以减少代码的冗余
UnitTest使用
- 定义一个实现加法操作的函数,并对该函数进行测试,使用pycharm在代码上点击鼠标右键,选择使用UnitTest运行
# test01_add.py
import unittest
def add(x, y):
return x + y
# 必须要集成TestCase类
class TestAdd(unittest.TestCase):
# 定义测试方法1
def test01_add(self):
result = add(1, 1)
print('result1=', result)
# 定义测试方法2
def test02_add(self):
print('result2=', result)
result = add(2, 2)
TestSuite
测试套件可以将多条测试用例集合在一起,就是一个TestSuite使用注:TestSuite需要配合TestRunner才能被执行
- 实例化:suite = unittest.TestSuite() (suite:为TestSuite实例化的名称)
- 添加用例:suite.addTest(ClassName("MethodName"))(ClassName:为类名;MethodName:为方法名)
- 添加扩展:suite.addTest(unittest.makeSuite(ClassName))(搜索指定ClassName内test开头的方法并添加到测试套件中)
import unittest
def ssuumm(a,b):
return a+b
class Testssuumm(unittest.TestCase):
def test_s1(self):
res = ssuumm(1,1)
self.assertEqual(res,2)
def test_s2(self):
res = ssuumm(3,1)
self.assertEqual(res,4)
# 添加多个测试套件,运行测试用例
import unittest
from test import Testssuumm
#实例化测试套件对象
suite = unittest.TestSuite()
# 添加测试用例
suite.addTest(Testssuumm("test_s1"))
suite.addTest(Testssuumm("test_s2"))
#实例化运行器对象
runner = unittest.TextTestRunner()
#运行测试套件
runner.run(suite)
Fixture
Fixture是对一个测试用例环境的初始化和销毁操作
- 方法级别使用运行一次测试方法就会运行一次 setUp 和 tearDown
- setUp(self):方法初始化时执行
- tearDown(self):方法销毁时执行
- 类级别的使用每个测试类只会 运行一次 setUpClass 和 tearDownClass
- setUpClass(cls):类初始化时执行
- tearDownClass(cls):类销毁时执行
import unittest
def ssuumm(a,b):
return a+b
class Testssuumm(unittest.TestCase):
# 类级别,初始化、前置处理,测试开始时只执行一次
@classmethod
def setUpClass(cls) -> None:
print('类初始化')
# 类级别,销毁、后置处理,测试结束时只执行一次
@classmethod
def tearDownClass(cls) -> None:
print('类销毁')
# 每个测试方法执行时都会执行此函数
def setUp(self) -> None:
print('方法初始化')
# 每个测试方法结束时都会执行次函数
def tearDown(self) -> None:
print('方法销毁')
def test_s1(self):
res = ssuumm(1,1)
self.assertEqual(res,2)
def test_s2(self):
res = ssuumm(3,1)
self.assertEqual(res,4)
UnitTest断言
让程序代替人为判断测试程序执行结果是否符合预期结果的过程,UnitTest 中提供了非常丰富的断言方法,但是常用的也就那么几个,并且使用起来也比较简单
import unittest
def ssuumm(a,b):
return a+b
class Testssuumm(unittest.TestCase):
def test_s1(self):
res = ssuumm(1,1)
self.assertEqual(res,2)
def test_s2(self):
res = ssuumm(3,1)
self.assertIn(str(res),'123456')
def test_s3(self):
res = ssuumm(3,1)
self.assertTrue(res==4)
UnitTest参数化
通过参数的方式来传递数据,从而实现数据和脚本分离,也可以把测试数据定义到数据文件或者数据库中,针对同一个测试方法,可以实现用例的重复执行,减少代码冗余,提高测试效率,unittest测试框架,本身不支持参数化,但是可以通过安装 unittest 扩展插件rameterized 来实现
- 环境安装:
pip3 install parameterized
import unittest
from parameterized import parameterized
def ssuumm(a,b):
return a+b
def get_data():
return [(1,2),(2,1)]
class Testssuumm(unittest.TestCase):
@parameterized.expand([(1,2),(2,1)])
def test_s1(self,a,b):
print(a)
print(b)
res = ssuumm(a, b)
self.assertEqual(res,3)
@parameterized.expand(get_data())
def test_s2(self,a,b):
print(a)
print(b)
res = ssuumm(a, b)
self.assertEqual(res,3)
生成HTML测试报告
测试脚本执行完后,可以生成以HTML格式的测试报告
import unittest
from test import Testssuumm
from HTMLTestRunner import HTMLTestRunner
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(Testssuumm))
filename = "report-{}.html".format(time.strftime("%Y-%m-%d %H:%M:%S"))
with open(filename,'wb') as f:
runner = HTMLTestRunner(f,title='testreport')
runner.run(suite)
达达商城登录接口测试
编写测试用例代码 - 未传参
import unittest
from dadatest import Testdada
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(Testdada))
runner = unittest.TextTestRunner()
runner.run(suite)
- 测试代码部分
import json
import unittest
import requests
class Testdada(unittest.TestCase):
def setUp(self) -> None:
self.url = 'http://127.0.0.1:8000/v1/tokens'
def test_login1(self):
data = {
'username':'111111',
'password':'111111',
'cart':'null',
}
res = requests.post(self.url,data=json.dumps(data))
self.assertEqual(res.json().get('code'),200)
def test_login2(self):
data = {
'username':'eee',
'password':'111111',
'cart':'null',
}
res = requests.post(self.url,data=json.dumps(data))
self.assertEqual(res.json().get('code'),20100)
def test_login3(self):
data = {
'username':'111111',
'password':'00',
'cart':'null',
}
res = requests.post(self.url,data=json.dumps(data))
self.assertEqual(res.json().get('code'),20200)
编写测试用例代码 - 传参
import unittest
import requests
import json
from parameterized import parameterized
# 构造测试数据
def build_data1():
test_data = []
file = './data/login.json'
with open(file, encoding='utf-8') as f:
json_data = json.load(f)
for case_data in json_data:
username = case_data.get('username')
password = case_data.get('password')
code = case_data.get('code')
test_data.append((username, password, code))
return test_data
def build_data2():
db = pymysql.connect('localhost','root','123456','test_dadashop_db', charset='utf8')
cur = db.cursor()
sele = 'select * from dadashop_login_tab'
cur.execute(sele)
db_data = cur.fetchall()
test_data = []
for case_tuple in db_data:
username = case_tuple[2]
password = case_tuple[3]
code = case_tuple[5]
test_data.append((username, password, code))
return test_data
class Testdada(unittest.TestCase):
def setUp(self) -> None:
self.url = 'http://127.0.0.1:8000/v1/tokens'
@parameterized.expand(build_data1())
def test_login1(self, username, password, code):
data = {
'username':username,
'password':password,
'cart':'null',
}
res = requests.post(self.url,data=json.dumps(data))
self.assertEqual(res.json().get('code'),code)
@parameterized.expand(build_data2())
def test_login2(self, username, password, code):
data = {
'username':username,
'password':password,
'cart':'null',
}
res = requests.post(self.url,data=json.dumps(data))
self.assertEqual(res.json().get('code'),code)
- json数据
[
{
"desc": "登录成功",
"username": "chaogege",
"password": "123456",
"carts": "null",
"code": 200,
"msg": "success"
},
{
"desc": "用户名错误",
"username": "chaoerror",
"password": "123456",
"carts": "null",
"code": 20100,
"msg": "username is wrong"
},
{
"desc": "密码错误",
"username": "chaogege",
"password": "123error",
"carts": "null",
"code": 20101,
"msg": "password is wrong"
}
]
- mysql数据库sql
create database test_dadashop_db default charset utf8;
use test_dadashop_db;
create table dadashop_login_tab(
id int primary key auto_increment,
case_desc varchar(100),
username varchar(100),
password varchar(100),
carts varchar(50),
code int,
msg varchar(200)
);
insert into dadashop_login_tab values(1,"登录成功","chaogege","123456","null", 200, "success"),(2,"用户名错误","chaoerror","123456","null", 20100, "username is wrong"),(3,"密码错误","chaogege","123error","null", 20101, "password is wrong");
0 条评论