并发的意义:
是以并发为手段进行的测试行为,通过测试行为发现接口在并发场景下导致的功能问题
类型
1.点层面上:同一时间做某件事
例如: 早上7:30,所有的学生都升国旗
2.线层面上:一个时间做不同的事,但同时对服务器产生压力
例如:一个时间点,有的学生跳皮筋,有的玩足球
并发测试不等同于性能测试,性能测试也只是并发测试一个小类
并发测试分类
功能并发测试
解析:要先进行测试单业务功能场景的并发测试,再进行混合业务功能场景的并发测试
目的: 为验证系统功能是否符合需求说明书的要求
性能并发测试
解析:同时满足某些系统性能指标的前提下,让被测对象承担不同的工作量,以评估被测对象的最大处理能力及是否存在缺陷。
目的:为验证系统性能指标是否符合需求规格说明书的要求
稳定性并发测试
解析:判断测试系统的长期稳定运行的能力
目的:为验证系统稳定性是否符合需求规格说明书的要求
异常性并发测试
解析:模拟系统在较差、异常资源配置下运行,以评估被测对象在资源不足的情况下的工作状态。
目的:为验证系统的异常响应机制是否满足需求规格说明书的要求
设计思路整理
第一步,需求分析
1.首先找到并发测试对象,了解需求测试的功能有哪些,可以按照业务功能整理,不必深入细节
2.其次描述测试对象的重要性,如严格要求质量的核心功能,高频使用的功能,占用系统资源较多的功能
3.最后进行测试对象拆分,例如购买商品拆分为成:搜索商品,锁定库存,提交订单,发送支付指令,接受处理支付结果,业务流水,短信及站内信通知,VX推送结果
环境需求分析
明确重点测试对象,预先设置基础数据及大量历史数据,模拟真实环境
性能指标需求分析
分析性能指标是否合理。可以从历史数据的几个方面考虑
TPS,页面访问量,并发请求数 (判断需求指标是否合理,安排优先级)
测试方案设计
测试策略
1.并发测试的准备工作:请求顺序,请求之间互相调用关系,数据流向,有没有调用外部系统等后。需要明确重点测试对象,预先设置基础数据及大量历史数据,模拟真实环境等。
- 对于性能并发测试:同时满足某些系统性能指标的前提下,让被测对象承担不同的工作量,以评估被测对象的最大处理能力及是否存在缺陷
3.对于稳定性并发测试:判断测试系统的长期稳定运行的能力。该策略强度较小,一般趋向于客户现场日常状态下的压力强度
4.对于异常性并发测试:模拟系统在较差,异常资源配置下运行,如人为降低系统工作环境所需的资源,网络带宽,系统内存,数据帧等,以评估在资源不足情况下的工作状态
总结:不同的测试阶段,测试人员关注的测试目的也是不同。所以对测试人员来说,测试思想才是最重要,有了测试思想,才会有好的测试方案
测试方案
以两轮测为例:
第一轮测试,以接口文档驱动测试,测试步骤及要点如下
1.编写接口测试代码:核对每个接口传入参数控制:长度限制,格式,必填项限制,正常值范围限制等。同时,确认报错提示信息是否准确,到位。
2.异常数据测试:如渠道值设置负数,属性设置不存在,网络断开,数据库锁表等情况,检查数据是否出现异常;
3.逐个接口进行并发事务测试:检查账户金额,用户流水,对账流水数据。核对数据与用例调用结果是否一致;
4.复合接口并发测试:将各种充值、交易类型的接口按照一定的顺序进行并发,校验账户收支金额、流水是否与用例调用结果是否一致
第二轮测试:以业务场景驱动测试,测试步骤及要点如下
1.统一动作并发:相同订单并发支付,并发退款;
2.混合交易场景:秒杀抢购,集中退货、到货确认
3.绕过页面操作:通过抓包,抛送异常值进行交易测试
指标分析
整理并发需求
需求内容: 中午和晚上是订餐的高峰期,所以会有很大的并发订单量。为了保证订单成功率、响应速度等因素,我们要对高峰期的订单量进行并发性能测试。
性能指标需求:
1.打开速度<3s,订单提交成功<5s;
2.订单成功率达到99.5%以上;
3.在100个并发用户的高峰期,订单处理能力至少达到900TPS
提取性能指标
以100个外卖订单为例,需要提取的并发指标;
1.并发订单数
2.成功订单数
3.成功订单响应时间
4.订单成功率
5.成功订单的总响应时间
6.成功订单的平均响应时间
7.TPS
性能指标分析
1.并发订单数:及自定义的并发数,把并发100次,设置为10个线程,每个循环10次
2.成功订单数:就是获取响应值为成功的请求,先定义一个个success_count ,初始值为0,如果成功的话执行+1;
3.订单成功率:成功订单数/总的订单数成功
4.订单总响应时间:每个成功订单的响应时间之和,所以我们定义一个sum_time,初始值为0.00,然后把每次成功的响应时间加起来成功
5.订单平均响应时间:成功订单总响应时间/成功订单数;
6.TPS:成功并发数/成功订单平均响应时间;
7.订单响应时间:在请求之前,获取一次时间,在断言成功之后,再次获取一次时间,这样二者之差,就是订单的响应时间。
# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2021-3-6
"""
import hashlib
import threading
from time import *
from datetime import datetime,timedelta
import requests
import json
'''初始化全局变量'''
#自定义全局变量需要的线程数,10
thread_num = 10
#自定义全局变量每个线程需要循环的数量,10
one_worker_num = 10
#设定最开始的总时间
sum_time = 0.00
#设定最开始的成功连接数
success_count = 0
''' 后台登录常规操作'''
username = 'carldj'
password = hashlib.md5(b'123456').hexdigest() #设置密码,且是md5加密方式
url = "http://www.xxx.com/energy/user/login/"
form_data = {"username":username,"password":password}
login_response = requests.post(url,data=form_data)
c = login_response.cookies
'''订单发送请求'''
def order():
#引用全局变量
global c
global sum_time
global success_count
#获取执行发送订单请求前时间
t1 = time()
#设定url、form_data进行创建订单
url1 = "http://www.xxx.com/energy/create_order/"
from_data1 = {"restaurant_id":1136,
"menu_item_total":'12.00',
"menu_item_data": [{'id':2667868,'p':22,'q':3}]
}
make_responst = requests.post(url1,data=from_data1,cookies = c)
#获取请求结果
res = make_responst.text
#结果转换成字典赋值给变量id
id = json.loads(res)['order_id']
#断言判断是否提交成功
assert id != " "
su_time =datetime.now()+ timedelta(hours=1)
#设定url、form_data进行生成订单
url2 = "http://www.xxx.com/energy/place_order/"
from_data2 = {"restaurant_id": id,
"customer_name": 'carl_dj',
"mobile_number":username,
"delivery_address":"address message",
"pay_type":'cash',
"preorder":su_time
}
place_responst = requests.post(url2, data=from_data2, cookies=c)
res = place_responst.text
#追加断言,判断结果是否有"success",有的话,说明订餐成功
assert res == " success"
print("订餐成功")
#订单成功后,再次获取一下时间
t2 = time()
#获取订单的响应时间
res_time = t2-t1
#把响应时间写入txt文件
result = open("E:\Private Folder\res.txt","a") #路径直接写死,也可用os.path 来写路径
result.write("成功订单响应时间:" + str(res_time)+ '\n')
result.close()
#也可以使用with打开文件,好处是不用关心文件是否关闭
# with open ("E:\Private Folder\res.txt","a") as result1:
# print(result1.read())
#把每次成功订单数累加到全局变量sum_time中
sum_time = sum_time + res_time
#把每次获取的成功订单数做累加,添加到全局变量success_count中
success_count = success_count +1
'''嵌套指定循环次数的order()函数'''
def working()
global one_worker_num
for i in range(0,one_worker_num):
order()
'''自定义main()函数,来执行多线程'''
def main():
global thread_num
#自定义一个空的数组,用来存放线程组
threads = []
#设置循环次数
for i in range(thread_num):
#将working()函数存放到线程中
t = threading.Thread(target=working,name="T"+ str(i))
#设定守护线程
t.setDaemon(True)
threads.append(t)
#启动循环执行
for t in threads:
t.start()
##设置阻塞线程
for t in threads:
t.join()
if __name__ == "__main__":
main()
total_order = thread_num*one_worker_num
avg_time = sum_time/success_count
'''执行完之后,需要把数据写入到txt文件中'''
#订单并发总数
result.write("并发订单数:"+ str(total_order)+ "\n")
#成功并发数
result.write("成功并发数:"+ str(success_count) + "\n")
#订单成功率
result.write("订单成功率:"+ str(success_count/total_order*100)+ "%" + "\n")
#成功订单响应时间
result.write("成功订单总响应时间:"+ str(sum_time)+"\n")
#成功订单平均响应时间
result.write("成功平均响应时间:"+str(sum_time/success_count)+"\n")
#TPS事务数/秒
result.write("TPS:"+str(success_count/avg_time) + "\n") #tps = 并发成功数/平均响应时间
result.close()