大道至简,知易行难;知行合一,止于至善。
谨以此文,献给在测试路上前进的各位同学~~
一:性能测试理论知识 (ps:我们先来了解下性能测试理论方面知识)
1.1:性能测试及其目的性能测试的定义:
通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。负载测试和压力测试都属于性能测试,两者可以结合进行。
性能测试的手段:
是通过模拟真实业务从而向服务器发送大量并发请求进而对被测系统产生负载,分析被测系统在不同压力下的表现。
我们进行性能测试的常见目的如下:
a:评估系统的性能(在局域网测试环境或生产环境下,通过测试结果的分析评估当前系统的服务级别)。
b: 定位性能瓶颈(通过性能测试找出影响系统整体性能的关键步骤或过程,为系统调优提供方向性依据)。
c:验证调优结果(通过比对优化后和优化前的测试结果,确认性能优化策略是否生效)。
1.2性能测试的种类细分1.2.1压力测试:
通过逐步增加系统负载,测试系统性能的变化,并最终确定在什么负载条件下系统性能处于失效状态来获得系统能提供的最大服务级别的测试。
压力测试是逐步增加负载,使系统某些资源达到临界点。
1.2.2负载测试:
通过逐步增加系统负载,测试系统性能的变化,并最终确定在满足性能指标的前提下,系统所能够承受的最大负载量的测试。
1.2.3稳定性测试:
通过给系统加载一定的业务压力(如CPU资源在70%~90%的使用率)的情况下,运行一段时间,检查系统是否稳定。因为运行时间较长,所以通常可以测试出系统是否有内存泄露等问题。
1.2.4:容量测试:
在一定的软、硬件条件下,在数据库中构造不同数量级的记录数量,通过运行一种或多种业务场景,在一定虚拟用户数量的情况下,获取不同数量级别的性能指标,从而得到数据库能够处理的最大会话能力、最大容量等。
1.2.5配置测试:
通过对被测试软件的软硬件配置的测试。配置测试能充分利用有限的软硬件资源,发挥系统的最佳处理能力,同时可以将其与其他性能测试类型联合应用,为系统调优提供参考。
1.3性能测试的实施流程(PS:在实施性能测试的过程中,整体工作流程是1:分析性能测试需求--2:设计性能测试方案3:开发性能测试脚本-4:搭建性能测试环境-5:执行测试-:6:分析结果后多轮测试进行验证优化-7:编写性能测试报告-8:编写性能测试总结报告)
二:性能需求分析(ps:以我之前做过的一个小需求逐步开始吧~~ )
目前公司开发人员15名,测试人员7名;使用TeamFoundation进行文档和测试用例以及bug的管理,团队考虑使用开源版禅道系统代替现有的teamfoundation。
此次性能测试活动目标如下:
1:能否使用禅道开源版(8.0.1)代替TeamFoundation进行项目活动管理。
2:评估禅道开源版(8.0.1)在一定的服务器硬件配置(cpui5 3.3GHz+内存8G)下的最大负载。
(ps:分析团队历史数据)
每版本测试用例数统计:
(ps:在12个迭代版本中 最多新建用例1832个)
单日提交bug数统计:
每小时提交Bug数统计:
以近一年来12个版本的数据进行分析,团队现状如下:
版本最大Case数:1832 单个版本最大Bug数:113
每月Release一个新版本,即:每22个工作日进行一次发布。
在每次releae过程中 编写测试用例的时间为:3天 执行测试的时间为 7天(一轮回归测试)。
按峰值大致算出团队目前各场景的事务数如下:
(ps:测试过程中对峰值tps也需要留20%的富余)
团队成员对业务及禅道环境的要求:
(ps:性能需求分析是性能测试流程中的第一步,如果这一步做好了 接下来的测试方案设计,脚本开发,测试执行,测试报告都会轻松很多; 反推也是成立的,如果不清楚需求是什么 后面多的一切都是白做!
另外:收集需求数据的途径有1:运维拉取生产环境的历史数据。2:参考竞品。3:对数据增量可以进行容量建模。
切记一点:千万别用什么所谓的二八原则,没有数据依据一切都是胡扯!)
三:性能测试方案设计(ps:完整的性能测试方案 直接拿去用吧~ 嘿嘿~)
3.1. 测试目的、范围与目标3.1.1 测试目的本次性能测试的主要目的在于:
测试已完成系统的综合性能表现,检验交易或系统的处理能力是否满足系统运行的性能要求;
发现交易中存在的性能瓶颈,并对性能瓶颈进行修改;
模拟发生概率较高的单点故障,对系统得可靠性进行验证;
3.1.2. 测试功能范围序号
业务名称
优先级
备注
1
登录系统
中
2
添加测试用例
高
3
执行用例
高
4
提交Bug
高
5
解决Bug
高
6
关闭Bug
高
8
确认Bug
高
9
10
3.1.3. 测试指标范围/***明确列出说明本次测试需要关注的测试指标的定义及范围,不需要关注的测试指标也应列出。下面的内容供参考。***/
本次性能测试需要获得的性能指标如下:
交易的响应能力:即在单交易负载和模拟生产交易情况的混合场景负载压力情况下,系统的响应时间。
每秒处理事务数:即应用系统在单位时间内完成的交易量(TPS)。
系统可支持的并发用户数量。
本次性能测试的限制性指标为:
系统资源使用情况:在正常压力下,应用服务器和数据库服务器的CPU、Memory占用率应分别低于80%、80%,数据库存储空间和文件系统空间占用率应低于80%。
交易的成功率:交易成功率不低于99.5%。
本次性能测试不需要关注的指标:
业务流程/路径覆盖率。
业务数据的完整、正确性。
其他诸如系统易用性、可管理性等属于专项测试的内容。
3.1.4. 测试目标///***明确本次测试各功能项的测试指标需要达到的测试目标,该目标须由项目组提出或最终确认。***///
针对不同类型交易的单业务事务平均响应时间
针对不同类型交易的单业务事务TPS值
在负载情况下的单业务事务平均响应时间
在负载情况下的单业务事务TPS值
在负载情况下的系统综合TPS值
3.2 测试资源3.2.1. 系统生产环境物理架构///***说明本项目生产环境的物理架构,可以以物理架构图或网络拓扑图的方式。***///
3.2.2. 性能测试环境物理架构///*说明本项目性能测试环境的物理架构,可以以物理架构图的方式。*///
可使用Visio画出测试环境和生产环境的网络拓扑图。
测试环境的网络拓扑图(单Web服务器+单数据库服务器)很简单在此省略。
3.2.3. 性能测试环境与生产环境资源对比说明本项目测试环境与生产环境的差异,确定性能测试环境的软硬件资源,包括待测系统各组成部分的配置。下表供参考,非强制使用。
服务器
性能测试环境(规划)
生产环境(规划)
硬件配置
软件配置及IP
硬件配置
软件配置
Web&DB服务器
Cpu:i5
Disk:500G
Memory:8G
192.168.10.206
Cpu:i3
Disk:500G
Memory:8G
Os:Win7(64位)
WebServer:Apache2.4
DB:MySql5.5
PHP: 5.4.19
负载生成服务器
Cpu:i3
Disk:500G
Memory:8G
Net:100Mb局域网
192.168.10.188
OS:Win7(64位)
LR 11
--
--
3.3. 测试准备3.3.1. 测试环境安装/*说明本次测试的测试环境安装情况。*/
XAMPP集成环境:
控制面板 1.2.6
phpmyadmin版本:Version 4.0.8
php版本:PHP 5.4.19 (cli) (built: Aug 21 2013 01:12:03)
apache版本:Server version: Apache/2.4.4 (Win32)
mysql版本:Ver 5.5.32
负载机:LoadRunner 11(patch3+patch4)(192.168.10.206)
3.3.2. 测试工具/*说明本次测试使用到的测试工具和监控工具。*/
浏览器:IE11,Chrome43
协议抓包:HttpWatch9.3
性能脚本:LoadRunner 11
监控工具:Monyog MySQL和LoadRunner11 Controller。
测试数据图表生成:ECharts
3.3. 测试脚本、数据及其预验证/**说明本次测试的测试脚本、测试数据以及混合场景的交易配比情况等。**/
3.3.1:基础测试数据系统用户:开发角色的用户40个 测试角色的用户20个。
项目:3个项目(Storage,Training,Clinical)
3.3.2:脚本及其验证Script1:登录禅道(zentao)脚本HttpWatch抓去登录禅道的请求:
登录请求返回的内容中 没有标识登录是否成功的字符
所以:插入检查点时检查登录请求中从服务器返回的内容里是否包括/zentao/index字符串。
登录禅道事务中参数UserName取值为测试+开发用户名。
代码语言:javascript代码运行次数:0运行复制Action()
{
lr_think_time(4);
lr_start_transaction("登录禅道");
//插入检查点
web_reg_find("Text=/zentao/index ","SaveCount=is_contain_server",LAST);
web_submit_data("user-login.html_2",
"Action=http://192.168.10.188/zentao/user-login.html",
"Method=POST",
"TargetFrame=",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/user-login.html",
"Snapshot=t2.inf",
"Mode=HTML",
ITEMDATA,
"Name=account", "Value={UserName}", ENDITEM,
"Name=password", "Value=123456", ENDITEM,
"Name=referer", "Value=", ENDITEM,
LAST);
//手动判断登录事务是否成功
if(atoi(lr_eval_string("{is_contain_server}"))>0)
{
lr_end_transaction("登录禅道",LR_PASS);
}
else
{
lr_end_transaction("登录禅道",LR_FAIL);
}
web_url("index.html",
"URL=http://192.168.10.188/zentao/index.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/user-login.html",
"Snapshot=t3.inf",
"Mode=HTML",
LAST);
return 0;
}
Script2:添加用例成功添加测试用例时 会从服务器返回添加用例是否成功的标识
插入检查点 手动判断添加用例事务是否成功
代码语言:javascript代码运行次数:0运行复制Action()
{
//点击测试 进入测试Tab页
web_url("qa",
"URL=http://192.168.10.188/zentao/qa/",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/my/",
"Snapshot=t4.inf",
"Mode=HTML",
LAST);
//点击用例 进入用例页面
web_url("用例",
"URL=http://192.168.10.188/zentao/testcase-browse-3.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-browse.html",
"Snapshot=t5.inf",
"Mode=HTML",
LAST);
//点击新建用例
web_url("建用例",
"URL=http://192.168.10.188/zentao/testcase-create-3-0-0.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/testcase-browse-3.html",
"Snapshot=t6.inf",
"Mode=HTML",
LAST);
web_url("story-ajaxGetProductStories-3-0-11-0-false-noclosed-50.html", "URL=http://192.168.10.188/zentao/story-ajaxGetProductStories-3-0-11-0-false-noclosed-50.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/testcase-create-3-0-0.html",
"Snapshot=t7.inf",
"Mode=HTML",
LAST);
lr_think_time(39);
//添加事务-添加测试用例
lr_start_transaction("添加测试用例");
//添加检查点
web_reg_find("Text=success","SaveCount=addcase_result",LAST);
web_submit_data("testcase-create-3-0-0.html",
"Action=http://192.168.10.188/zentao/testcase-create-3-0-0.html",
"Method=POST",
"EncType=multipart/form-data",
"TargetFrame=",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/testcase-create-3-0-0.html",
"Snapshot=t8.inf",
"Mode=HTML",
ITEMDATA,
"Name=product", "Value=3", ENDITEM,
"Name=module", "Value=11", ENDITEM,
"Name=type", "Value=feature", ENDITEM,
"Name=stage[]", "Value=", ENDITEM,
"Name=stage[]", "Value=system", ENDITEM,
"Name=title", "Value=testcase{CaseNum}", ENDITEM,
"Name=pri", "Value=3", ENDITEM,
"Name=precondition", "Value=", ENDITEM,
"Name=steps[]", "Value=step1", ENDITEM,
"Name=expects[]", "Value=expectresult{CaseNum}", ENDITEM,
"Name=steps[]", "Value=", ENDITEM,
"Name=expects[]", "Value=", ENDITEM,
"Name=steps[]", "Value=", ENDITEM,
"Name=expects[]", "Value=", ENDITEM,
"Name=keywords", "Value=", ENDITEM,
"Name=files[]", "Value=", ENDITEM,
"Name=labels[]", "Value=", ENDITEM,
LAST);
//手动判断添加测试用例是否成功
if(atoi(lr_eval_string("{addcase_result}"))>0)
{
lr_end_transaction("添加测试用例",LR_PASS);
}
else
{
lr_end_transaction("添加测试用例",LR_FAIL);
}
web_url("testcase-browse-3--byModule-11.html",
"URL=http://192.168.10.188/zentao/testcase-browse-3--byModule-11.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/testcase-create-3-0-0.html",
"Snapshot=t9.inf",
"Mode=HTML",
LAST);
return 0;
}
Script3:执行用例HttpWatch抓取执行用例请求:
执行用例的请求中 从服务器端并未返回事务是否成功的标识
所以插入检查点验证是否返回selfClose来手动判断执行用例事务的成功与否
每个测试用例可多次执行 无权限问题
代码语言:javascript代码运行次数:0运行复制Action()
{
lr_think_time(8);
//添加事务-执行测试用例
lr_start_transaction("执行用例");
//添加检查点
web_reg_find("Text=selfClose","SaveCount=execuatecase_result",LAST);
web_submit_data("execuate_case",
"Action=http://192.168.10.188/zentao/testtask-runCase{case_str}.html",
"Method=POST",
"TargetFrame=",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/testtask-runCase{case_str}.html",
"Snapshot=t8.inf",
"Mode=HTML",
ITEMDATA,
"Name=steps[5]", "Value=pass", ENDITEM,
"Name=reals[5]", "Value=", ENDITEM,
"Name=case", "Value={CaseIndex}", ENDITEM,
"Name=version", "Value=1", ENDITEM,
LAST);
//手动判断事务是否成功
if(atoi(lr_eval_string("{execuatecase_result}"))>0)
{
lr_end_transaction("执行用例",LR_PASS);
}
else
{
lr_end_transaction("执行用例",LR_FAIL);
}
web_url("testcase-browse-3.html",
"URL=http://192.168.10.188/zentao/testcase-browse-3.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/testcase-browse-3.html",
"Snapshot=t9.inf",
"Mode=HTML",
LAST);
return 0;
}
Script4:提交Bug代码语言:javascript代码运行次数:0运行复制Action()
{
lr_think_time(50);
//添加事务-提交Bug
lr_start_transaction("提交Bug");
//添加检查点
web_reg_find("Text=success","SaveCount=submitbug_result",LAST);
web_submit_data("bug-create-3-0-moduleID=0.html_3",
"Action=http://192.168.10.188/zentao/bug-create-3-0-moduleID=0.html",
"Method=POST",
"EncType=multipart/form-data",
"TargetFrame=",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-create-3-0-moduleID=0.html",
"Snapshot=t15.inf",
"Mode=HTML",
ITEMDATA,
"Name=product", "Value=3", ENDITEM,
"Name=module", "Value=11", ENDITEM,
"Name=project", "Value=1", ENDITEM,
"Name=openedBuild[]", "Value=trunk", ENDITEM,
"Name=assignedTo", "Value=dev{DevNum}", ENDITEM,
"Name=type", "Value=codeerror", ENDITEM,
"Name=os", "Value=win7", ENDITEM,
"Name=browser", "Value=ie11", ENDITEM,
"Name=title", "Value=BugTitle{Num}", ENDITEM,
"Name=severity", "Value=3", ENDITEM,
"Name=pri", "Value=3", ENDITEM,
"Name=steps", "Value=
[步骤]
\r\n[结果]
\r\n[期望]
", ENDITEM,"Name=story", "Value=", ENDITEM,
"Name=task", "Value=", ENDITEM,
"Name=mailto[]", "Value=", ENDITEM,
"Name=keywords", "Value=", ENDITEM,
"Name=files[]", "Value=", ENDITEM,
"Name=labels[]", "Value=", ENDITEM,
"Name=case", "Value=0", ENDITEM,
"Name=caseVersion", "Value=0", ENDITEM,
"Name=result", "Value=0", ENDITEM,
"Name=testtask", "Value=0", ENDITEM,
LAST);
//手动判断提交Bug是否成功
if(atoi(lr_eval_string("{submitbug_result}"))>0)
{
lr_end_transaction("提交Bug",LR_PASS);
}
else
{
lr_end_transaction("提交Bug",LR_FAIL);
}
web_url("bug-browse-3.html",
"URL=http://192.168.10.188/zentao/bug-browse-3.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-create-3-0-moduleID=0.html",
"Snapshot=t16.inf",
"Mode=HTML",
LAST);
return 0;
}
Script5:解决Bug代码语言:javascript代码运行次数:0运行复制Action()
{
web_url("bug-resolve-12.html",
"URL=http://192.168.10.188/zentao/bug-resolve-12.html?onlybody=yes",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-browse.html",
"Snapshot=t19.inf",
"Mode=HTML",
EXTRARES,
"Url=js/kindeditor/themes/default/default.png", "Referer=http://192.168.10.188/zentao/js/kindeditor/themes/default/default.css", ENDITEM,
"Url=theme/zui/css/min.css", "Referer=http://192.168.10.188/zentao/bug-resolve-12.html?onlybody=yes", ENDITEM,
LAST);
lr_think_time(10);
//添加事务-解决Bug
lr_start_transaction("解决Bug");
//添加检查点
web_reg_find("Text=selfClose","SaveCount=fixbug_result",LAST);
web_submit_data("bug-resolve-12.html_2",
"Action=http://192.168.10.188/zentao/bug-resolve-12.html?onlybody=yes",
"Method=POST",
"EncType=multipart/form-data",
"TargetFrame=hiddenwin",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-resolve-12.html?onlybody=yes",
"Snapshot=t20.inf",
"Mode=HTML",
ITEMDATA,
"Name=resolution", "Value=fixed", ENDITEM,
"Name=duplicateBug", "Value=", ENDITEM,
"Name=resolvedBuild", "Value=trunk", ENDITEM,
"Name=resolvedDate", "Value={TimeNow}", ENDITEM,
"Name=assignedTo", "Value={TesterList}", ENDITEM,
"Name=files[]", "Value=", "File=Yes", ENDITEM,
"Name=labels[]", "Value=", ENDITEM,
"Name=comment", "Value=已经解决 请验证", ENDITEM,
LAST);
//手动判断解决Bug事务是否成功
if(atoi(lr_eval_string("{fixbug_result}"))>0)
{
lr_end_transaction("解决Bug",LR_PASS);
}
else
{
lr_end_transaction("解决Bug",LR_FAIL);
}
web_url("bug-browse.html",
"URL=http://192.168.10.188/zentao/bug-browse.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-browse.html",
"Snapshot=t21.inf",
"Mode=HTML",
LAST);
return 0;
}
Script6:关闭Bug代码语言:javascript代码运行次数:0运行复制Action()
{
web_url("bug-close.html",
"URL=http://192.168.10.188/zentao/bug-close-{Bug_Index}.html?onlybody=yes",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-browse-3-0-assignToMe-0.html",
"Snapshot=t6.inf",
"Mode=HTML",
EXTRARES,
"Url=js/kindeditor/themes/default/default.png", "Referer=http://192.168.10.188/zentao/js/kindeditor/themes/default/default.css", ENDITEM,
"Url=theme/zui/css/min.css", "Referer=http://192.168.10.188/zentao/bug-close-{Bug_Index}.html?onlybody=yes", ENDITEM,
LAST);
lr_think_time(19);
//开始事务-关闭Bug
lr_start_transaction("关闭Bug");
//添加检查点
web_reg_find("Text=selfClose","SaveCount=closebug_result",LAST);
web_submit_data("bug-close",
"Action=http://192.168.10.188/zentao/bug-close-{Bug_Index}.html?onlybody=yes",
"Method=POST",
"TargetFrame=hiddenwin",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-close-{Bug_Index}.html?onlybody=yes",
"Snapshot=t7.inf",
"Mode=HTML",
ITEMDATA,
"Name=comment", "Value=关闭Bug", ENDITEM,
LAST);
//手动判断事务是否成功
if(atoi(lr_eval_string("{closebug_result}"))>0)
{
lr_end_transaction("关闭Bug",LR_PASS);
}
else
{
lr_end_transaction("关闭Bug",LR_FAIL);
}
web_url("bug-browse-3-0-assignToMe-0.html",
"URL=http://192.168.10.188/zentao/bug-browse-3-0-assignToMe-0.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.188/zentao/bug-browse-3-0-assignToMe-0.html",
"Snapshot=t8.inf",
"Mode=HTML",
LAST);
return 0;
}
Script7:确认 Bug代码语言:javascript代码运行次数:0运行复制//开始事务
lr_start_transaction("确认Bug");
//插入检查点
web_reg_find("Text=selfClose","SaveCount=SureBugResult",LAST);
web_submit_data("bug-confirmBug.html_2",
"Action=http://192.168.10.206:81/zentao/bug-confirmBug-{BugId}.html?onlybody=yes",
"Method=POST",
"TargetFrame=hiddenwin",
"RecContentType=text/html",
"Referer=http://192.168.10.206:81/zentao/bug-confirmBug-{BugId}.html?onlybody=yes",
"Snapshot=t9.inf",
"Mode=HTML",
ITEMDATA,
"Name=assignedTo", "Value=dev{DevIndex}", ENDITEM,
"Name=pri", "Value=3", ENDITEM,
"Name=mailto[]", "Value=", ENDITEM,
"Name=comment", "Value=可重现 确认为Bug ", ENDITEM,
LAST);
//手动判断事务结果
if(atoi(lr_eval_string("{SureBugResult}"))>0)
{
lr_end_transaction("确认Bug",LR_PASS);
}
else
{
lr_end_transaction("确认Bug",LR_FAIL);
}
web_url("bug-browse.html",
"URL=http://192.168.10.206:81/zentao/bug-browse.html",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=http://192.168.10.206:81/zentao/bug-browse.html",
"Snapshot=t10.inf",
"Mode=HTML",
LAST);
4. 测试方法及案例设计说明本次测试的测试方法(内容)及测试案例、测试场景设计。下面章节供参考。
系统登录场景设计如下:
场景1
登陆系统
目的
测试多人同时登陆系统的性能情况
方法
Vusers和资源
并发用户数
CPU/Mem/Disk数据
Apache数据
MySql数据
网络使用率
事务平均响应时间
10人
20人
添加测试用例场景设计如下:
场景2
添加测试用例
目的
测试多人同时添加测试用例的性能情况
方法
Vusers和资源
并发用户数
CPU/Mem/Disk数据
Apache数据
MySql数据
网络使用率
事务平均响应时间
10人
提交Bug场景设计如下:
场景3
提交Bug
目的
测试多人同时提交Bug的性能情况
方法
Vusers和资源
并发用户数
CPU/Mem/Disk数据
Apache数据
MySql数据
网络使用率
事务平均响应时间
10人
解决Bug 场景设计如下:
场景4
解决Bug
目的
测试多人同时更改Bug为fixed状态的性能情况
方法
Vusers和资源
并发用户数
CPU/Mem/Disk数据
Apache数据
MySql数据
网络使用率
事务平均响应时间
15人
30人
关闭Bug场景设计如下:
场景5
关闭Bug
目的
测试多人同时关闭Bug的性能情况
方法
10个Vusers并发(测试人员才使用关闭Bug功能)
Vusers和资源
并发用户数
CPU/Mem/Disk数据
Apache数据
MySql数据
网络使用率
事务平均响应时间
10人
确认bug场景设计如下:
场景6
确认bug
目的
测试多人同时确认Bug的性能情况
方法
20个Vusers并发(测试人员才使用关闭Bug功能)
Vusers和资源
并发用户数
CPU/Mem/Disk数据
Apache数据
MySql数据
网络使用率
事务平均响应时间
20人
登录负载测试:
场景7
登陆系统
目的
评估登录场景最大TPS
方法
120-270人登陆系统的TPS
Vusers和资源
并发用户数
CPU使用率
Mem使用率
TPS
网络使用率
事务平均响应时间
100-140人
100-180人
180-220人
添加用例最大TPS:
场景8
添加用例
目的
评估添加用例场景最大TPS
方法
120-270人添加用例的TPS
Vusers和资源
并发用户数
CPU使用率
Mem使用率
TPS
网络使用率
事务平均响应时间
100-140人
100-180人
180-220人
执行用例最大TPS:
场景9
执行用例
目的
评估执行用例场景最大TPS
方法
120-270人执行用例的TPS
Vusers和资源
并发用户数
CPU使用率
Mem使用率
TPS
网络使用率
事务平均响应时间
100-140人
100-180人
180-220人
提交Bug最大TPS:
场景10
提交Bug
目的
评估提交Bug场景最大TPS
方法
120-270人提交bug的TPS
Vusers和资源
并发用户数
CPU使用率
Mem使用率
TPS
网络使用率
事务平均响应时间
100-140人
100-180人
180-220人
解决Bug最大TPS:
场景11
解决Bug
目的
评估解决Bug场景最大TPS
方法
120-270人解决bug的TPS
Vusers和资源
并发用户数
CPU使用率
Mem使用率
TPS
网络使用率
事务平均响应时间
100-140人
100-180人
180-220人
关闭Bug最大TPS:
场景12
关闭Bug
目的
评估关闭Bug场景最大TPS
方法
120-270人关闭bug的TPS
Vusers和资源
并发用户数
CPU使用率
Mem使用率
TPS
网络使用率
事务平均响应时间
100-140人
100-180人
180-220人
4.1. 基准测试在测试环境经过确认,脚本预验证之后对本次测试涉及的全部联机交易做基准测试。目的是验证测试脚本及后台环境、初步检查交易本身是否存在性能缺陷。
测试方法:
使用LoadRunner测试工具向192.168.10.188服务器发送交易请求,接收并分析返回结果。拟采用10Vuser负载执行,取交易的平均响应时间作为衡量指标,并计算吞吐量
4.2. 单场景负载测试对本次测试涉及的全部联机交易完成基准测试后,分别执行单交易负载测试。目的是获得交易本身的性能表现,诊断交易是否存在性能缺陷。
测试方法:
使用LoadRunner测试工具向206服务器发送交易请求,接收并分析返回结果。
4.3:稳定性测试多场景压测系统7x24小时
5. 测试输出/*说明在测试完成后需要输出的阶段性成果,作为检验测试的衡量标准。
当测试完成以后,需提交的主要文档包括,但不仅限于:
《禅道开源版v8.0.1性能测试方案》
《禅道开源版v8.0.1性能测试记录及问题跟踪表》
《禅道开源版v8.0.1性能测试报告》
6. 测试进度计划在测试工作量估算数据的基础上,考虑现有的资源情况,对资源进行具体安排,根据项目整体进度计划,列出进度表,即是谁在什么时间内完成什么任务。下表供参考,非强制使用。
序号
名称
责任人
工期
开始时间
完成时间
1
禅道开源版v8.0.1性能测试
X工作日
2016-2-22
2016-2-28
1.1
测试准备
x工作日
2016-2-22
2016-2-22
1.1.1
测试实施方案制定
x 工作日
2016-2-22
2016-2-22
1.1.2
测试主机、数据库环境就绪
x 工作日
2016-2-22
2016-2-22
1.1.4
性能测试业务数据就绪
x 工作日
2016-2-22
2016-2-22
1.1.5
服务部署就绪
x 工作日
2016-2-22
2016-2-22
1.1.6
测试脚本编制、参数就绪
x 工作日
2016-2-23
2016-2-23
1.2
基准、单交易负载测试
x工作日
2016-2-23
2016-2-24
1.2.1
单交易基准测试
x 工作日
2016-2-23
2016-2-24
1.4
负载测试
x工作日
2016-2-24
2016-2-26
1.5
测试总结
x 工作日
2016-2-27
2016-2-28
7. 实施风险及规避措施/*风险管理是对影响项目测试的各种可能发生的风险进行估计,以及对风险的发生几率和严重程度进行估计,并按照估计结果对风险进行排序*/
风险描述
风险发生的可能性
风险对项目的影响
责任人
规避方法
测试环境与运行环境差距较大,通过测试得到的运行参数偏差。在试运行阶段需要重新进行参数验证。
中
低
根据测试结果反推生产环境,由运维同学监控数据并作及时调整。
测试数据量和数据库中预埋数据量较小,通过测试时间推算的批量处理交易的运行时间满足要求,生产环境下数据不能满足。
中
高
等比缩放,减小系统数据容量。
由于发现较严重缺陷引发较长时间的程序修改,或因环境准备、数据等原因造成测试进度延迟。
高
中
预留测试环境维护时间,不定时备份测试环境和数据
四:脚本开发详解(PS:不管是录制还是手工开发脚本,对协议报文体系的了解是基本功 必不可少的,我们先熟悉下http协议的报文体系哈)
4.1协议报文体系抓包工具有很多,重量级的有wireshark 次之有fiddler,charles和Omnipeek,轻量级的小插件当然也可以 比如firebug和httpwatch都很轻巧好用 抓包必备神器。
4.1.1 http协议报文体系4.1.1.2 http协议的请求格式我们现在从上向下依次看下请求报文里都是些什么鬼
POST 表示http的方法
(ps:http的方法有以下几种
GET 请求获取Request-URI所标识的资源
POST 在Request-URI所标识的资源后附加新的数据
HEAD 请求获取由Request-URI所标识的资源的响应消息报头
PUT 请求服务器存储一个资源,并用Request-URI作为其标识
DELETE 请求服务器删除Request-URI所标识的资源
TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT 保留将来使用
OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求)
/WebServices/TrainTimeWebService.asmx/getStationAndTimeByStationName 请求的资源对象(这里是一个webservice接口中的一个方法)
HTTP/1.1 表示所使用的http协议版本号
代码语言:javascript代码运行次数:0运行复制Accept: text/html, application/xhtml+xml, */*
请求报文头部的Accept表示浏览器端可接受的媒体类型。
(PS: Accept: */* 代表浏览器可以处理所有返回的媒体类型;
Accept: text/html 代表浏览器可以接受服务器回发的类型为 text/html ;如果服务器无法返回text/html类型的数据,服务器应该返回一个406错误(non acceptable) )
代码语言:javascript代码运行次数:0运行复制Referer: http://ws.webxml.com.cn/WebServices/TrainTimeWebService.asmx?op=getStationAndTimeByStationName
告诉服务器当前请求是从哪个页面链接过来的,服务器可以获得一些信息用于处理。
(PS:
我们抓包可以看到每次请求的Header中,基本上都有Referer值,值就是当前页面的url。
浏览器所做的工作相当简单。如果在页面A上点击一个链接,然后进入B页面,浏览器就会在请求中插入一个带有页面A的Referer首部;自己输入的URL中不会保护Referer首部)。
代码语言:javascript代码运行次数:0运行复制Accept-Language: zh-CN
指定客户端可接受或优选哪些语言(比如,内容所使用的自然语言)
代码语言:javascript代码运行次数:0运行复制User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
客户端应用程序信息(浏览器内核,操作系统内核信息等)
代码语言:javascript代码运行次数:0运行复制Content-Type: application/x-www-form-urlencoded
指定请求报文中对象的媒体类型(MIME)。
(PS:我们常见的媒体类型如下
1. text:用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式的;默认是text/plain;
2. multipart:用于连接消息体的多个部分构成一个消息,这些部分可以是不同类型的数据;默认是multipart/mixed;
3. application:用于传输应用程序数据或者二进制数据;默认是application/octet-stream;
4. message:用于包装一个E-mail消息;
5. image:用于传输静态图片数据;
6. audio:用于传输音频或者音声数据;
7. video:用于传输动态影像数据,可以是与音频编辑在一起的视频数据格式。)
代码语言:javascript代码运行次数:0运行复制Accept-Encoding: gzip, deflate
指定客户端接受的编码方式。通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate,compress;q=0.5 这不是指字符编码)
代码语言:javascript代码运行次数:0运行复制Connection: Keep-Alive
声明与服务器的连接机制,如keep-alive等
主要用于有代理网络环境,这样服务器或其他代理就可以指定不应传递的逐跳首部了。
代码语言:javascript代码运行次数:0运行复制Content-Length: 30
请求的实体报文长度
代码语言:javascript代码运行次数:0运行复制DNT: 1
表示是否开启DNT(Do not track)功能,1表示开启,0表示关闭。
代码语言:javascript代码运行次数:0运行复制Host: www.webxml.com.cn
声明需要请求URI的主机信息。
代码语言:javascript代码运行次数:0运行复制Pragma: no-cache
Pragma头域用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache-Control:no-cache相同。
代码语言:javascript代码运行次数:0运行复制theCityName=%E6%9D%AD%E5%B7%9E
(PS:这里是请求的body(主体)部分,我也把=号后面的字符串解码后是杭州)
http协议的请求头部总结:
常用的标准请求头包括下面几个:
Accept,声明哪种相应是可接受的,如text\plain、application\json等。Cache-Control,声明缓存控制机制,如no-cache声明不做缓存。Connection,声明与服务器的连接机制,如keep-alive等。Cookie,声明Cookie信息。Content-Type,声明请求体的MIME类型。Host,声明需要请求URI的主机信息。If-Match,声明匹配这个请求的Key,如果服务器的ETag与这个Key一致,则认为这个请求的资源没有发生改变,客户端可以选择从还从中加载这个请求。Origin,声明跨域请求的时候支持什么域名进行访问。User-Agent,声明发出这个请求的客户端的描述,如果是浏览器发出的请求,可以根据这个头判断是哪个浏览器的哪个版本。非标准请求头包括下面几个:
X-Request-With,通常通过这个头告诉服务器这个请求是XMLHttpRequest发送的。DNT,表示是否开启DNT(Do not track)功能,1表示开启,0表示关闭。Front-End-Https,是微软用的一个自定义头,与负载均衡有关。Proxy-Connection,与标准头Connection一致,是早期HTTP协议的产物。4.1.1.2 http协议的响应格式响应报文头部信息
代码语言:javascript代码运行次数:0运行复制HTTP/1.1 200 OK
http协议的版本,服务器返回的响应状态码,
代码语言:javascript代码运行次数:0运行复制Date: Thu, 18 Aug 2016 06:37:22 GMT
报文创建的日期和时间。
代码语言:javascript代码运行次数:0运行复制Server: Microsoft-IIS/6.0
Web服务器的信息
代码语言:javascript代码运行次数:0运行复制X-Powered-By: ASP.NET
声明该响应是通过哪种语言生成的
代码语言:javascript代码运行次数:0运行复制X-AspNet-Version: 2.0.50727
代码语言:javascript代码运行次数:0运行复制Cache-Control: private, max-age=0
指定响应遵循的缓存机制
PS:Cache-Control主要有以下几种类型:
代码语言:javascript代码运行次数:0运行复制(1) 请求Request:
[1] no-cache ---- 不要读取缓存中的文件,要求向WEB服务器重新请求
[2] no-store ---- 请求和响应都禁止被缓存
[3] max-age: ---- 表示当访问此网页后的max-age秒内再次访问不会去服务器请求,其功能与Expires类似,只是Expires是根据某个特定日期值做比较。一但缓存者自身的时间不准确.则结果可能就是错误的,而max-age,显然无此问题.。Max-age的优先级也是高于Expires的。
[4] max-stale ---- 允许读取过期时间必须小于max-stale 值的缓存对象。
[5] min-fresh ---- 接受其max-age生命期大于其当前时间 跟 min-fresh 值之和的缓存对象
[6] only-if-cached ---- 告知缓存者,希望内容来自缓存且并不关心被缓存响应是否是最新的.
[7] no-transform ---- 告知代理,不要更改媒体类型.
(2) 响应Response:
[1] public ---- 数据内容皆被储存起来,就连有密码保护的网页也储存,安全性很低
[2] private ---- 数据内容只能被储存到私有的cache,仅对某个用户有效,不能共享
[3] no-cache ---- 可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端
[4] no-store ---- 请求和响应都禁止被缓存
[5] max-age: ----- 本响应包含的对象的过期时间
[6] Must-revalidate ---- 如果缓存过期了,会再次和原来的服务器确定是否为最新数据,而不是和中间的proxy
[7] max-stale ---- 允许读取过期时间必须小于max-stale 值的缓存对象。
[8] proxy-revalidate ---- 与Must-revalidate类似,区别在于:proxy-revalidate要排除掉用户代理的缓存的。即其规则并不应用于用户代理的本地缓存上。
[9] s-maxage ---- 与max-age的唯一区别是,s-maxage仅仅应用于共享缓存.而不应用于用户代理的本地缓存等针对单用户的缓存. 另外,s-maxage的优先级要高于max-age.
[10] no-transform ---- 告知代理,不要更改媒体类型,比如jpg,被改成png
代码语言:javascript代码运行次数:0运行复制Content-Type: text/xml; charset=utf-8
响应报文的媒体类型。
Content-Length: 7325
服务器响应报文body长度。
常用的http协议的响应头信息总结:
代码语言:javascript代码运行次数:0运行复制1:Access-Control-Allow-Origin,声明这个响应可以参与到哪个域的跨域访问中。*表示可以参与到任何域的跨域访问。
2:Allow,声明这个HTTP响应是使用哪个HTTP方法,如GET、POST等。如果是一个不支持的HTTP方法,则会返回错误码405 Method not allowed。
3:Content-Type,声明这个响应的MIME类型。
4:ETag,声明这个响应版本的key,可以标识一个资源是否有改变过(参考请求头If-Match)。
5:Pragma,声明这个响应是否支持缓存,可以设置no-cache禁用这个响应的缓存。
6:Refresh,声明这个响应在特定时间后刷新或者跳转到新的URL。
7:Status,响应的状态码。
非标准的响应头包括下面几个:
代码语言:javascript代码运行次数:0运行复制X-Frame-Options,声明防止Clickjacking攻击的参数,如deny就是防止响应被渲染在iframe里面,而sameorigin就是防止响应在非本域的页面中渲染。
X-Content-Type-Options,帮助IE能不识别MIME类型不对的stylesheet和script,也用于Chrome下载其扩展。
X-Powered-By,声明该响应是通过哪种语言生成的。
4.1.1.3 已定义的状态码及其意义状态码:
原因短语:
含义:
100
Continue(继续)
收到请求的起始部分,客户端应该继续请求
101
Switching Protocols(切换协议)
服务器正根据客户端的指示协议切换成update首部列出的协议
200
OK
服务器已经成功处理请求
201
Created(已创建)
对那些要服务器创建对象的请求来说 资源已创建完毕
202
Accept(已接受)
请求已接受,但服务器尚未处理
203
Non-Authoritative Informatino(非权威信息)
服务器成功处理,只是实体头部来自资源的副本
204
No Content(没有内容)
响应报文包含一些首部和一个状态行 但不包含实体的主题内容
205
Reset Content(重置内容)
浏览器应该重置当前页面上所有的HTML表单
206
Partial Content(部分内容)
部分请求成功
300
Multiple Choices(多项选择)
客户端请求了实际指向多个资源的URL
301
Moved Permanently(永久搬离)
请求的URL已移走,响应中应包含一个Location URL说明资源新位置
302
Found(已找到)
请求的URL临时性移走。
303
See Other(参考其他)
告诉客户端应用另一个URL
304
Not Modified(未修改)
资源未发生过改变
305
Use Proxy(使用代理)
必须通过代理访问资源,代理的位置在Location首部
307
Temporary Redirect(临时重定向)
400
Bad Request(坏请求)
服务器不能理解收到的请求,即发出了异常请求。
401
Unauthorized(未授权)
在客户端获得资源访问权之前,先要进行身份认证
403
Forbidden(禁止)
服务器拒绝了请求
404
Not Found(未找到)
服务器无法找到所请求的URL
405
Method Not Allowed(不允许使用的方法)
请求中有一个所请求的URI不支持的方法
406
Not Acceptable(无法接受)
407
Proxy Authentication Required(要求进行代理认证)
408
Request Timeout(请求超时)
409
Conflict(冲突)
发出的请求在资源上造成了一些冲突
410
Gone(已消失)
请求的资源曾经在服务器存在
411
Length Required(要求长度指示)
服务器要求在请求报文中包含Content-Length头部
412
Precondition Failed(先决条件失败)
服务器无法满足请求的条件
413
Request Entity Too Large(请求实体太大)
客户端发送的请求所携带的请求URL超过了服务器能够或者希望处理的长度
414
Request URI Too Long(请求URI太长)
415
Unsupported Media Type(不支持的媒体类型)
服务器无法理解或不支持客户端所发送的实体的内容类型
416
Requested Range Not Satisfiable(所请求的范围未得到满足)
417
Expectation Failed(无法满足期望)
500
Internal Server Error(服务器内部错误)
501
Not Implemented(未实现)
502
Bad Gateway(网关故障)
作为代理或网关使用的服务器遇到了来自响应链中上游的无效响应
503
Service Unavailable(未提供次服务)
服务器目前无法为请求提供服务
504
Gateway Timeout(网关超时)
505
HTTP Version Not Supported(不支持的HTTP版本)
4.2 录制生成的脚本4.2.1 录制PC端应用脚本1:配置浏览器网络代理(以Chrome51为例)
”设置””网络””更改代理服务器设置”
代理地址设置为127.0.0.1 端口要使用未被占用的端口(此处为9999)
2:配置LoadRunner Record Options中的Port Mapping
代码语言:javascript代码运行次数:0运行复制“Recording Options””Port Mapping””New Entry”
Target Server:目标服务器的IP地址
比如:我想访问118主机上的Easystudy服务 这里IP地址填写如上图。
Traffic Forwarding:勾选允许通过本地端口,这里的本地端口要填写一个没被占用的(和浏览器端的设置一致9999)。
注意事项如下:
1: Application type要设置为Win32 Applications
通过Port Mapping录制脚本的时候
Application type已经不是Internet Application了,这里需要选择为Win32 Applications,以为录制的时候 LR会启动一个”LoadRunner Socket Proxy Starter”的小窗口。
2:Program to record
必须填写为LR安装目录下bin\wplus_init_wsock.exe文件的绝对路径。
3:Capture level的设置
默认情况下是Socket level data,如果录制完成后脚本为空,
需要更改为Socket level and WinNet level data 然后重新录制即可。
4.2.2抓包移动端APP应用脚本1:共享笔记本上的wifi(我这里用的是猎豹的wifi共享小工具)。
2:设置手机端wifi代理
3:开启Fiddler抓包(下图以滴滴出行为例)
(PS:好好学下Fiddler这款神器哈~)
4.3 全手工开发脚本4.3.1 C-Vuser脚本开发(ps:了解了前面的http报文的结构,接着要乘胜追击 一起来看下如何根据抓包内容手工开发性能脚本吧)
4.3.1.1 操作报文头部的函数为下一个请求添加头部信息:
代码语言:javascript代码运行次数:0运行复制web_add_header(“Accept-Language”,” zh-CN”)
为后续的所有请求添加头部信息:
代码语言:javascript代码运行次数:0运行复制web_add_auto_header("Accept–Encoding", "gzip");
我也也可以在录制脚本的时候通过设置来确定哪些请求头的信息需要保存:
Record Options---Advanced---Headers
另外:web_cleanup_auto_headers()停止向后面的 HTTP 请求中添加自定义标头。
web_revert_auto_header
停止向后面的 HTTP 请求中添加特定的标头,但是生成隐性标头。
4.3.1.2过滤指定URL请求的函数代码语言:javascript代码运行次数:0运行复制web_add_filter(”action=Include/Exclude”,” Attribute=xxx”,LAST)
设置下一个请求中包含或排除的URL
代码语言:javascript代码运行次数:0运行复制web_add_auto_filter(”action=Include/Exclude”,” Attribute=xxx”,LAST)
为后续所有请求设置包含或排除的URL
4.3.1.2操作cookie的函数代码语言:javascript代码运行次数:0运行复制web_add_cookie("lang=zh-cn;theme=default;windowWidth=1584;windowHeight=353; sid=0lab6lvctq0hh2flm48qq520h2");
增加一个新的cookie或修改现有的cookie
代码语言:javascript代码运行次数:0运行复制web_remove_cookie删除指定的cookie(和web_add_cookie对应)
web_add_cookie_ex("Cookie=client_id=China127B; path=/; expires=Wednesday, 09-Nov-2001 23:12:40 GMT; domain=", "Insert=Alt", "AllowEmptyDomain=yes", LAST );
添加自定义的cookie
代码语言:javascript代码运行次数:0运行复制web_cleanup_cookies()
清空当前用户保存的所有cookie
4.3.1.4 模拟报文发送的函数Web_url函数格式:
代码语言:javascript代码运行次数:0运行复制web_url("Login",
"URL=http://localhost/somostorage/UserInfo/Login",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t7.inf",
"Mode=HTML",
EXTRARES,
"Url=../Images/Login/logo-center.png", "Referer=http://localhost/somostorage/Content/themes/login/gelogin.css", ENDITEM,
"Url=../Images/Login/gebutton.png", "Referer=http://localhost/somostorage/Content/themes/login/gelogin.css", ENDITEM,
"Url=../images/icomoon/icomoon.ttf", "Referer=http://localhost/somostorage/Content/UIForIpadAndIphone/css/glyphs.css", ENDITEM,
"Url=../Images/Images/user_btn.png", "Referer=http://localhost/somostorage/Content/UIForIpadAndIphone/css/index.css", ENDITEM,
"Url=../Content/UIForIpadAndIphone/images/mainview/jiantou.png", "Referer=http://localhost/somostorage/Content/UIForIpadAndIphone/css/index.css", ENDITEM,
"Url=../Images/Buttons/page_go.png", "Referer=http://localhost/somostorage/Content/themes/bluestyle/cont.css", ENDITEM,
"Url=../Content/UIForIpadAndIphone/images/mainview/bg_fileheader.png", "Referer=http://localhost/somostorage/Content/UIForIpadAndIphone/css/index.css", ENDITEM,
"Url=../Content/UIForIpadAndIphone/images/mainview/4.png", "Referer=http://localhost/somostorage/Content/UIForIpadAndIphone/css/index.css", ENDITEM,
"Url=../Content/UIForIpadAndIphone/images/imageview/mleft.png", "Referer=http://localhost/somostorage/Content/UIForIpadAndIphone/css/index.css", ENDITEM,
"Url=../Content/UIForIpadAndIphone/images/imageview/mright.png", "Referer=http://localhost/somostorage/Content/UIForIpadAndIphone/css/index.css", ENDITEM,
LAST);
Web_submit_data函数格式:
HTTP协议post方法常见的有4种方式,所以脚本的格式也不尽相同:
代码语言:javascript代码运行次数:0运行复制1:application/x-www-form-urlencoded
这应该是最常见的 POST 提交数据的方式了。浏览器的原生