Web应用特色是做同样的事情可以有多种的操作路径,同样的有多种浏览方式可以到达相同的地方。对用户来说这是一件好事情,但对于测试者来说,这将使测试变得更加复杂,我们不仅需要知道用户在做什么,还要知道他们是如何做的。本文将论述如何使用Rational
TestStudio来设计和脚本化测试套件(suite),解决用户可能选择不同浏览路径的问题,使得测试结果能更准确地代表用户的感受。
压力测试专家可能会辩论说测试脚本开发这种层次的详述仅仅是一项额外的工作,并不会增加项目的价值。如果我们的目标是进行压力测试的话,我会认同这种说法。在本系列的第一章中,压力测试定义为“在不包括用户延迟的高用户负载下,各种脚本的组合测试,但对确定用户体验并不合适”。回顾第一章,本系列专注于外部用户的性能体验与客户满意度之间的联系。作为负载测试的一部分,确定用户体验的关键是模拟个别用户的行为模式,而压力测试并不需要关注这些。(请阅读《网站压力测试》(Website
Stress-Testing),该书对两者的区别做了明确的说明)。下文将论述如何将这些个别用户模式应用到用户组(群)中。
本系列的第二章论述了如何模拟个别用户延迟,类似的,本文将同样的基于在Noblestar多年的测试经验并以TestStudio进行示范。本文适用于TestStudio所有等级的用户,但更适用于中高级用户。我建议你将本文提到的观点应用到一个你清楚其用户负载、用户模式和用户体验度量的网站上进行有效性验证,将用户体验结果与实际结果进行比较。
1.确定用户模式
为了准确地收集到用户体验度量,你必须在适当的时间,将适当的压力应用到系统适当的部分上。这听起来好像很复杂,但实际并非如此。你可以从确定个别的用户模式开始。我会介绍一些不同的方法,并以在线书店为例说明如何确定用户模式。
假设你是书店的经营者,希望从性能视角来了解用户在网站上购书的情况。首先需要回答一个问题:有一个回头客(return
user)登录网站后想买一个最新的畅销书New York Times,他需要通过哪些页面来完成?我们可以得到如图1的购书路线图(如何生成这个图将在下文详细论述)。
图1 购买小说的主要路线图
上图的例子中,所有用户从首页开始,通过三种方式其中的一种来进行:
- 搜索书名或作者
- 到小说分类中慢慢查找
- 直接在畅销书闪烁的链接中找到它
每种路径的用户比例上图已经标示出来了,所有用户都买了书。这是一个非常简单的例子,但这些概念可以很容易地应用到复杂的情况。注意,上图每条路径确定了一种活动,但并未包括活动的具体步骤或访问的页面。
上图的实现方法对你的测试目的来说也许已经足够准确,但也许不够。方法如下:在为此模型选择了购书网站作为例子后,我认为最通常的用户活动就是买书了。接着访问这个网站,找出买书的所有可能的路径,以一个网站用户或测试者的身份,通过自己的经验和直觉,评估自己访问每条路径的可能性。最后完成了上面的图形。
运用你的直觉和最佳猜测的方法,有两方面的好处:(1)在用科学的方法识别用户模式之前,已经对网站有了一个直观的感受;(2)以此来验证用更科学的方法获得的用户模式,也很有意义。由于我无法获取到更科学的信息(例如日志文件,流量监控软件,系统设计文档,系统管理权限等等),所以我只能到此为止,但你可以做得更好。
如果你正在进行一个真实的测试,那么可以用以下的任一种方法来测定用户模式:
- 如果测试的是一个已发布的网站,那么可以通过流量监控软件(如WebTrends、LiveStats)的统计信息来测定实际的数据和分布情况。这些软件在正确配置之后,能够以易于理解的图表方式告诉你每种用户活动的比例和行为路径。如果没有流量监控软件的话,也可以通过日志文件中的页面点击信息来测定用户活动分布。第四章将会对此进行更详细的论述。
- 如果有网站技术规格说明书的话,它会告诉你用户最可能进行哪些活动,以及用户期望在哪些导航路径的指引下进行操作。有时功能测试脚本或测试用例也有助于用户活动和路径的确定。这些资料不会直接告诉你实际上用户是如何浏览网站的,但你能够间接地知道系统是如何与用户进行交互的。
- 如果测试的网站是新开发的从来没有发布过,那么你可以尝试着从相似网站的管理员处收集到用户使用模式的信息。当然,这类网站通常最有可能的就是你们的竞争对手,因此它们的管理员可能并不会那么合作。
- 如果以上三种方法都行不通的话,你可以进行简单的内部实验,通过员工、客户、朋友或家庭成员的帮助来测定不同用户是如何浏览网站的。如我在第二章提到的,对于从没有发布过的网站来说,我发现这是一种非常高效的数据收集方法,同时也是验证使用其它方法收集到的数据的一种有效途径。
2.用TestStudio模拟用户模式
Rational TestStudio提供了很多种创建和模拟虚拟测试用户的方法,但就是因为有太多的方法,有时也会变得难以选择。下文将以在线书店为例,描述并论证三种用户模式的模拟方法,这同时也是个人觉得最简单和最通用的方法:
- 全路径脚本方法:为每条浏览路径录制单独的脚本,并在测试套件(suite)中分配适当的用户比例。
- 路径片段方法:为图形中的每一段线录制单独的脚本,并在测试套件中正确的组织起来。
- 灵活的脚本方法:在录制的脚本中创建灵活的脚本,处理大部分浏览路径中的少数例外。
每个方法都可以与其它方法结合起来使用,模拟更复杂的用户模式。每种方法都有其固有的优点和缺点,下文将会提到。
2.1.全路径脚本方法
三种方法中全路径脚本方法最容易描述和执行。这种方法为每个可能的浏览路径录制单独的端到端脚本,然后创建测试套件以适当的分布组织执行这些脚本。图2展示了购买小说的脚本中所包括的三种可能的路径。
图2 全路径脚本方法的购书路径
录制3个这样的脚本是相当简单的事情。请注意,图中主页访问和购书活动在所有的脚本中都是重复的,这会在其它方法的论述中再次提到。图3展示了脚本在TestManager测试套件中的用户比例情况。
图3 全路径脚本执行方法的测试套件
这是模拟简单用户模式的一个好方法,只需少量的脚本就能覆盖所有可能的用户路径。而且,也不需要手工修改任何脚本来模拟用户模式。由于每种用户都直接匹配一个脚本,当测试有安全的会话ID或cookie的站点时比较容易处理安全问题,也许这就是这种方法最大的优势。(我会在后续的文章中论述关于安全会话的处理)
但另一方面,这种方法也有较大的缺陷。首先,脚本中重复的功能意味着更多的开销,会降低当前测试环境下的虚拟测试用户数。如果你的测试实验室和我一样的话,你需要为这些消耗巨大的测试保留所有的资源,而你当然不希望硬件设备成为瓶颈而使得性能测试结果变得很糟糕。就个人而言,在大多数情况下我会尽量使每个套件的测试脚本数量少于12个。在不影响性能测试结果的情况下,你的主站点和代理站点的硬件越好,测试套件中就能容纳更多的脚本。
其次,还有关于动态数据方面的缺点。脚本中的冗余部分有时会包含一些每个虚拟用户都不同的数据(例如登录信息),可能会用到数据池。这会导致每个脚本都有独立或非常相似的数据池,以及用于控制这些数据逻辑的共享变量或持久变量。如果管理不当的话,这些将产生大量的开销。通常,尽可能地消除持久变量,并减少数据池的数量和大小,只产生必要的数据,是一个不错的做法。
最后,如果脚本中主页或者购书部分发生了变化,所有脚本都需要修改。如果变动较大的话,3个脚本都需要重新录制。对于已经稳定的已发布应用来说,这可能不会有这个问题,但如果测试的系统还在开发当中,那要好好考虑一下了。
2.2.路径片段方法
路径片段方法也是一种相当简单的方法。这种方法为每段路径录制一个独立的脚本(也称分割脚本),如图4所示。这些脚本按适当的顺序和分布组成可被执行的测试套件。
图4 路径片段方法的购书路径
如你所见,和全路径方法相比,这种方法多了两个脚本,但脚本中没有了重复的活动。图5展示了TestManager中代表我们的书店浏览模型的一种测试套件组织方法。
图5 路径片段方法的测试套件
这种方法只需更少的脚本,对于简单的用户模式来说相当不错。虽然多了两个脚本,但不会比全路径脚本方法需要更多的开销。在本在线书店的例子中,将产生五个实际的脚本,但每个都比全路径脚本方法产生更少的开销,所以总的开销其实差不多。这种方法真正的优点是通过没有冗余的脚本,消除多脚本访问同一个数据池,以及多个类似或同样的数据池的可能性。
这种方法同时也降低了当被测应用发生局部改变时重新录制脚本的可能性。对于采用迭代式开发和测试的应用来说,这将是一个巨大的好处。
这种方法有两个缺点:(1)可能需要用到共享变量,(2)如果脚本或场景运行不正常,调试会变得复杂,并导致共享变量的使用。如果场景的正确运行需要在两个脚本之间传递信息,则只能用共享变量来传递信息。这个复杂的主题会在以后的文章中提到。如果你将录制的分割脚本在场景中组合起来,但与录制时的顺序不同的话,脚本可能会运行不起来。在脚本录制前谁也不知道分割后的脚本是否能运行起来。如果不能正确回放,那通常是没有通过共享变量来传递信息的问题。
2.3.灵活的脚本方法
灵活的脚本方法能让你模拟在主要的浏览路径中的少量例外路径。例如,在我们的在线书店中,每条路径上的顾客可能都会打开其它信息窗口,也并不是所有的顾客最终都会买书。
图6 包含了少数例外的购书浏览路径
图中的虚线表示了一些偏离主浏览路径的活动。在Name Search路径中有10%的用户会看帮助,在Select
Best Seller路径中有25%的用户会阅读读者评论,在Fiction Books路径中有5%的用户会参与调查活动。而且,所有用户中的25%并没有买书就离开了。
在这种方法中,测试套件模型可以保留但必须重新考虑脚本。幸运的是,通过有计划地处理这些可选的偏离活动的脚本,避免了在测试套件中增加任何的全路径脚本、分割脚本、用户组等。我们只需简单地在分割的脚本中增加一些代码就能处理这些偏离。这些处理需要最基本的C编程经验,但我会和你一起做下去,这样的话即使你没有编程经验也能通过简单的复制、粘贴完成这些代码。
让我们来看看如何修改Name Search这段代码,以满足客户在搜索之前先看帮助这样的场景。列表1显示了满足场景的部分代码。
/*
->-> Session File Information <-<-
*/
#include <VU.h>
{
push Http_control = HTTP_PARTIAL_OK | HTTP_CACHE_OK | HTTP_REDIRECT_OK;
push Timeout_scale = 200; /* Set timeouts to 200% of maximum
response time */
push Think_def = "LR";
Min_tmout = 120000; /* Set minimum Timeout_val to 2 minutes
*/
push Timeout_val = Min_tmout;
DP1 = datapool_open("Name_Search_Seg");
datapool_fetch(DP1);
push Think_avg = 0;
start_time ["View Search Page"];
stop_time ["View Search Page"];
start_time ["View Help"];
stop_time ["View Help"];
start_time ["View Search Results"];
stop_time ["View Search Results"];
} |
列表1 查看帮助信息的Name Search脚本
那么我们如何模拟查看帮助的10%用户呢?这个简单,我们在每次脚本被调用时生成一个1~10之间的随机数字,当数字为1时将进行查看帮助的操作,否则跳过这一步。请看列表2。
->-> Session File
Information <-<-
*/
#include <VU.h>
int percent; /* Declare the variable that will hold the random
number */
{
push Http_control = HTTP_PARTIAL_OK | HTTP_CACHE_OK | HTTP_REDIRECT_OK;
push Timeout_scale = 200; /* Set timeouts to 200% of maximum
response time */
push Think_def = "LR";
Min_tmout = 120000; /* Set minimum Timeout_val to 2 minutes
*/
push Timeout_val = Min_tmout;
DP1 = datapool_open("Name_Search_Seg");
datapool_fetch(DP1);
push Think_avg = 0;
start_time ["View Search Page"];
stop_time ["View Search Page"];
percent = uniform(1,10); /* Select a random number between 1
and 10 */
if (percent = 1) /* If the selected number is 1, view help */
{
start_time ["View Help"];
stop_time ["View Help"];
} /* If the selected number is not 1, script resumes here */
start_time ["View Search Results"];
stop_time ["View Search Results"];
} |
列表2 模拟10%的用户查看帮助信息的脚本
其它3个可选路径也能够用类似的方法实现。
灵活的脚本方法对于处理这种不需要完成整个脚本,起始点和结束点相同的活动非常有用。这种方法的另一个很好的例子如用户的注册过程,对于初次使用的用户需要进行账号注册才能登录系统,但是,并非所有的用户都需要,因此,这也是脚本中需要考虑的。
比其它两种脚本方法来说,这种脚本方式实现起来更快并且所需的开销更少,同时冗余的代码也更少。把这种方法和其它的两种合起来一起使用,将能模拟几乎所有的用户模式。
这种方式的缺点在运行时就会显露出来。在录制过程中,有些socket可能会在if语句外面打开并在if语句里面引用,回放时如果if语句里面的脚本不执行的话会导致脚本执行失败。还有其它一些类似的问题。如果遇到这样的问题,你可以进行脚本调试,但这里我不会详细讨论。一般而言,弹出式的独立浏览窗口上的活动不会出现这种问题。
2.4.选择哪种方法
在大多数情况中,选择使用哪种方法来模拟用户使用模式只是一个个人偏好问题。上面已经提到,每种方法都有其优缺点,你只能根据具体的情况选择最佳的那种。在测试复杂的站点时,我通常会将3种脚本方法组合起来使用。
下面有一些选择方法供参考:
当站点满足以下情况时使用全路径脚本方法:
- 只有少量的浏览路径冗余
- 浏览路径中只有少量的datapool冗余
- 使用安全的session
- 通过URL的参数传递数据
当站点满足以下情况时使用路径片段方法:
- 有很大的浏览路径冗余
- 浏览路径中有大量的datapool冗余
- 有限的安全性
- 没有通过URL的参数传递数据
当站点满足以下情况时使用灵活脚本方法:
- 存在起止都在同一页面的可选浏览路径
- 存在弹出窗口的链接
3.练习
为了证明以上提到3种方法的简单和有效,下面以www.wmata.com站点(华盛顿市区交通运输的权威站点)为例,来创建用户使用模式的模型。选择这个网站的原因是它具备示例论证所需的各种条件属性,同时你不需要购买或注册任何东西,我也不需要考虑版权问题等。当然,如果你是为网站的所有者测试的话,就不会有这个问题了。你也可以在你喜欢的、含有弹出窗口的静态站点上验证这些方法。
在开始讲解之前,首先我假定你已经掌握了如何录制、回放VU脚本,如何在录制过程中插入Timer,以及如何用TestManager创建脚本套件。同时我也假定你已经阅读了本文的第2章,因此我会省去有关用户延迟时间方面的内容。下面的练习只需用1个用户,某些情况下只需2个用户就可以进行测试了。因此别对www.wmata.com网站施加大用户量的测试,否则你会被认为是恶意攻击并承担刑事责任,有言在先,我是不会承担任何责任的。
假设通过直觉我们确定用户使用模式如图7所示。
图7 WMATA网站上的用户路径
3.1.全路径脚本方法练习
使用全路径脚本方法来模拟用户路径,我们需要如图8的两个脚本。
图8 代表WMATA用户路径的全路径脚本
我们将通过点击适当的链接,添加注释、Timer、延迟等来录制这两个浏览路径,与第2章介绍的方法一样。
对于上面两个路径中的Plan a Trip路径的脚本录制,我们的旅途将从Vienna地铁站到Smithsonian地铁站,中间经过多个著名的博物馆。请按下面的步骤:
- 从主页开始录制
- 点击屏幕右侧的Riding Metro部分的系统地图链接
- 在橙色线路中点击最左边的车站,Vienna车站
- 点击右边的Riding Metro中的Stations下拉框
- 点击Smithsonian车站
- 停止录制
对于View General Information脚本,按以下步骤录制:
- 从主页开始录制
- 点击屏幕右方的About Metro
- 点击Metro Police链接
- 点击右边的Metro B2B中的Solicitation/awards
- 停止录制
现在我们可以在TestManager创建测试套件来执行这个场景了。点击Suites >
New Suite > Blank Performance Testing Suite来创建测试套件,接着通过User Groups
> Insert > User Group创建2个用户组,分别设置60%和40%的用户比例,在用户组的右键菜单中通过Insert
> Test Script插入相应的脚本。如图9所示。
图9 用于执行WMATA全路径脚本的测试套件
是不是很简单呢?好,我们继续吧。
3.3灵活脚本方法练习
如果你已经在做这个练习,或曾经访问这个网站的话,你可能会发现在WMATA网站的Plan a Trip path的最后一页上,有一个关于地铁站之间行程价格和时间的链接。出于好奇心你可能点击来看看。我们把这个可选的路径加到Plan
a Trip的脚本中,并假设其中50%的用户会关心行程的价格和时间的问题,如图12所示。
图12 添加了可选路径的WMATA用户路径
出于练习方面的考虑,我们重点关注Plan a Trip部分的模拟。我们先录制一个包含了Fares
and Times部分的脚本。这里使用的是全路径脚本方式,虽然也可以用脚本片段方式。
第一部分的脚本,可以用和前面一样的步骤。录制打开主页(www.wmata.com),点击System
map,点击Vienna stop,点击Stations,在下拉框中选择Smithsonian stop。接下来录制另一段脚本。点击Fares
& travel times between stations链接(这里会弹出一个新的浏览窗口,这也就是使用灵活脚本方法的最佳方式),在下拉列表中选择Vienna,点击Get
Fare Data点击,看完结果之后再点击关闭按钮,最后停止录制。
用第二章介绍的方法调整延迟时间后,把这段新录制的代码插入到前面的脚本中,形成一个满足新流程的脚本。代码3展示了部分的脚本。
#include <VU.h>
int percent; /* Declare the variable that will hold the random
number */
{
push Http_control = HTTP_PARTIAL_OK | HTTP_CACHE_OK | HTTP_REDIRECT_OK;
push Timeout_scale = 200; /* Set timeouts to 200% of maximum
response time */
push Think_def = "LR";
Min_tmout = 120000; /* Set minimum Timeout_val to 2 minutes
*/
push Timeout_val = Min_tmout;
push Think_avg = 0;
start_time ["Home Page"];
www_wmata_com = http_request ["Metro_T~001"] "www.wmata.com:80",
. . .
http_disconnect(www_wmata_com_1);
stop_time ["Home Page"];
delay(uniform(6000,14000));
start_time ["System Map"];
//set Think_avg = 17525;
www_wmata_com_2 = http_request ["Metro_T~014"] "www.wmata.com:80",
. . .
http_nrecv ["Metro_T~057"] 100 %% ; /* 8052 bytes
*/
stop_time ["System Map"];
delay(uniform(7000,12000));
start_time ["Vienna"];
//set Think_avg = 6650;
set Server_connection = www_wmata_com_4;
. . .
http_nrecv ["Metro_T~078"] 100 %% ; /* 15581 bytes
*/
stop_time ["Vienna"];
delay(uniform(9000,13000));
start_time ["Stations"];
//set Think_avg = 23514;
set Server_connection = www_wmata_com_5;
. . .
http_disconnect(www_wmata_com_4);
stop_time ["Stations"];
delay(uniform(9000,13000));
start_time ["Smithsonian"];
//set Think_avg = 13199;
set Server_connection = www_wmata_com_5;
. . .
http_disconnect(www_wmata_com_7);
stop_time ["Smithsonian"];
delay(uniform(9000,13000));
percent = uniform(1,10); /* Select a random number between 1
and 10 */
if (percent < 6) /* If the selected number is greater than
6, view fares */
{
start_time ["Fares and Times"];
//set Think_avg = 13199;
smartbenefits_wmata_com = http_request ["Metro_T~113"]
"smartbenefits.wmata.com:443",
. . .
http_disconnect(smartbenefits_wmata_com);
stop_time ["Fares and Times"];
delay(uniform(12000,15000));
start_time ["Get Fare Data"];
//set Think_avg = 19268;
smartbenefits_wmata_com_1 = http_request ["Metro_T~119"]
"smartbenefits.wmata.com:443",
. . .
http_disconnect(smartbenefits_wmata_com_1);
stop_time ["Get Fare Data"];
}
pop [Think_def, Think_avg, Timeout_val, Timeout_scale]; |
列表3:添加了查看费用信息的部分脚本
如同前面在线书店的例子一样,这种方法通过变量的随机值来控制脚本的执行路径。
给高级用户:灵活脚本方法看起来非常简单,但需要注意的是,if语句中浏览的是独立浏览器窗口的会好一些。这种脚本方法可以用在任何可选的导航路径,但前提是开始点和结束点在同一地方,对于个别的sockets(set
Server_connection和http_disconnect命令)来说需要手工维护。这个详细讨论起来就太多了,根据我个人的经验,如果没有非常必要的情况的话,另外录制一个脚本来覆盖那部分的用户模型会没那么痛苦。
4. 小结
本文介绍了3种模拟个别用户行为模式的方法,以及如何在Rational TestManager中使用。你可以使用全脚本路径方法、脚本片段方法和灵活脚本方法中的一种或者组合来有效地模拟任何的个别用户行为模式。本系列的下一篇文章将详细介绍如何使用TestManager创建测试套件来准确模拟全部的用户群,而不仅是个别用户。
5.参考
“Website Stress-Testing” by Serdar Yegulalp (ExtremeTech Web site)
6.附录(单词)
distinction:区别, 差别
simplistic:过分单纯化的
make sense:有意义
access to:有权使用
alternative:可供选择的办法
universally:普遍地,全体地,到处
inherent:固有的, 内在的, 与生俱来的
drawback:缺点, 障碍
redundant:多余的,冗余的
yield:出产, 生长, 生产
eliminate:排除, 消除
surefire:准不会有错的, 一定能达到目的的
Survey:调查,测量
in concert with:和...相呼应[合作]
leave out:省去,不考虑
out of curiosity:出于好奇心
original version:原著 |