整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

性能测试实例(二):构建

性能测试实例(二):构建

能测试实例:构建

设计阶段完成后,进入构建阶段,主要完成设计测试用例、开发测试脚本和确定监控模型。

首先设计测试用例,本实例中设计的测试用例包括登录和订票两个功能,登录的测试用例见表16-7

订票的测试用例见表 16-8

接着开发测试脚本,开发登录脚本时需要注意以下几个问题:

  • (1)当输入订票系统的地址时,客户端会向服务器端发送一个 SessionID 值,服务器端收到这个 SessionID 值后会返回一个 SessionID 值到客户端,而这个值是一个动态的值,所以在开发脚本时会进行关联。
  • (2)需要插入事务来获取登录所需要的时间,但需要注意的是,结束事务的状态不能直接写为自动,结束事务的状态应该由检查的结果来确定。
  • (3)设置检查点,确定登录是否成功,即检查登录的用户是否显示正确。当检查点正确时,将结束事务的状态设置为 PASS,否则设置事备的结束状态设置为 FAIL。
  • (4)对登录的用户名和密码进行参数化。


登录的脚本代码如下:

Action()
{
/* Registering parameter(s) from source task id 791
// {CSRule_1_UID2}="106229.040745241ftztVfzpciDDDDDDDcHitpQDHH"
// */
//关联
web_reg_save_param("CSRule_1_UID2",
"LB=userSession value=",
"RB=>",
"Ord=1",
"RelFrameId=1.2.1",
"Search=Body",
LAST);
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
LAST);
web_add_cookie("BAIDUID=EED4BF3C68FCA425464A7B08B3E6721B:FG=1; DOMAIN=passportso.baidu.com");
web_add_cookie("BDUSS=HZ3QTFxaVdJT2NaeVYzMUN4LVFHN2pkbGxSWGZ2fnJmblUybGhuOGtJOUJZRTlPQVFB
QUFBJCQAAAAAAAAAAApBEw8VdxMLYXJpdm5odWFuZwAAAAAAAAAAAAAAAAAAAAAAAAAAAADgGoV0AAA
AAOAahXQAAAAAcF1CAAAAAAAxMC42NS4yMkHTJ05B0ydOZ; DOMAIN=passportso.baidu.com");
web_add_cookie("USERID=379f52fe979c4447da48be6ab312; DOMAIN=passportso.baidu.com");
web_url("q",
"URL=http://passportso.baidu.com/checkuser/q?t=1311397506",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t2.inf",
"Mode=HTML",
LAST);
web_add_cookie("BAIDUID=EED4BF3C68FCA425464A7B08B3E6721B:FG=1; DOMAIN=fetch. im.baidu.com");
web_add_cookie("USERID=379f52fe979c4447da48be6ab312; DOMAIN=fetch.im.baidu.com");
web_url("ihaloader",
"URL=http://fetch.im.baidu.com/ihaloader?op=msgcount&charset=gbk&callback=WebIMHistMsg&refer=toolbar&un=arivnhuang",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t3.inf",
"Mode=HTML",
LAST);
lr_think_time(6);
//添加检查点
web_reg_find("Search=Body",
"Text=test11",
"SaveCount=regno",
LAST);
result=web_reg_find("Search=Body",
"Text=test11",
LAST);
web_submit_data("login.pl",
"Action=http://127.0.0.1:1080/WebTours/login.pl",
"Method=POST",
"TargetFrame=",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home",
"Snapshot=t4.inf",
"Mode=HTML",
ITEMDATA,
"Name=userSession", "Value={CSRule_1_UID2}", ENDITEM,
"Name=userName", "Value={user}", ENDITEM,
"Name=Password", "Value=1", ENDITEM,
"Name=JSFormSubmit", "Value=on", ENDITEM,
"Name=Login.x", "Value=53", ENDITEM,
"Name=Login.y", "Value=13", ENDITEM,
LAST);
//通过检查点来判断事务是否成功
if (atoi(lr_eval_string("{regno}"))>=1) {
lr_end_transaction("Login", LR_PASS);
}
else{
lr_end_transaction("Login", LR_FAIL);
}
web_url("SignOff Button",
"URL=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=1",
"TargetFrame=body",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t5.inf",
"Mode=HTML",
LAST);
return 0;
}


最后开发订票脚本,此时需要注意以下几个问题:

  • (1)对出发城市和到达城市进行参数化,出发城市和到达城市不能为同一城市。
  • (2)选择航班时为了更真实地模拟客户的行为,选择最便宜的航班。
  • (3)插入相关事务来获取订票的响应时间。


订票的脚本代码如下:

Action()
{
int i; //循环次数变量
int flagno;//标识位,最贵机票是数组中的第几个值
int min;//最贵机票
char cost[20];//当前机票的价格
char flightcost[20];
char flightnocost[20];
char flightelem[30];//航班
char flightno[30];
web_concurrent_end(NULL);
web_url("Search Flights Button",
"URL=http://127.0.0.1:1080/WebTours/welcome.pl?page=search",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t17.inf",
"Mode=HTTP",
LAST);
web_concurrent_start(NULL);
web_url("reservations.pl",
"URL=http://127.0.0.1:1080/WebTours/reservations.pl?page=welcome",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/welcome.pl?page=search",
"Snapshot=t18.inf",
"Mode=HTTP",
LAST);
web_url("nav.pl_3",
"URL=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=flights",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/welcome.pl?page=search",
"Snapshot=t19.inf",
"Mode=HTTP",
LAST);
web_concurrent_end(NULL);
web_url("button_next.gif",
"URL=http://127.0.0.1:1080/WebTours/images/button_next.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/reservations.pl?page=welcome",
"Snapshot=t20.inf",
LAST);
web_concurrent_start(NULL);
web_url("in_flights.gif",
"URL=http://127.0.0.1:1080/WebTours/images/in_flights.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=flights",
"Snapshot=t21.inf",
LAST);
web_url("home.gif",
"URL=http://127.0.0.1:1080/WebTours/images/home.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=flights",
"Snapshot=t22.inf",
LAST);
web_concurrent_end(NULL);
//创建机票价格的关联函数
web_reg_save_param( "WCSParam_Text1", "LB=center>$ ", "RB=", "Ord=All",
"IgnoreRedirections=Yes", "Search=Body", "RelFrameId=1", LAST );
//创建航班的关联函数
web_reg_save_param( "WCSParam_Text2", "LB=outboundFlight value=", "RB=;", "Ord=All",
"IgnoreRedirections=Yes", "Search=Body", "RelFrameId=1", LAST );
web_submit_data("reservations.pl_2",
"Action=http://127.0.0.1:1080/WebTours/reservations.pl",
"Method=POST",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/reservations.pl?page=welcome",
"Snapshot=t23.inf",
"Mode=HTTP",
ITEMDATA,
"Name=advanceDiscount", "Value=0", ENDITEM,
"Name=depart", "Value=Frankfurt", ENDITEM,
"Name=departDate", "Value=10/26/2011", ENDITEM,
"Name=arrive", "Value=Denver", ENDITEM,
"Name=returnDate", "Value=10/27/2011", ENDITEM,
"Name=numPassengers", "Value=1", ENDITEM,
"Name=seatPref", "Value=None", ENDITEM,
"Name=seatType", "Value=Coach", ENDITEM,
"Name=.cgifields", "Value=roundtrip", ENDITEM,
"Name=.cgifields", "Value=seatType", ENDITEM,
"Name=.cgifields", "Value=seatPref", ENDITEM,
"Name=findFlights.x", "Value=58", ENDITEM,
"Name=findFlights.y", "Value=5", ENDITEM,
LAST);
//初始化最贵机票,将第一个航班的机票设置为初始化的最贵机票
min=atoi(lr_eval_string("{WCSParam_Text1_1}"));
//初始化标识位,默认设置为 1
flagno=1;
//for 循环所有机票
for(i=2;i <=atoi(lr_eval_string("{WCSParam_Text1_count}"));i++){
sprintf(cost,"{WCSParam_Text1_%d}",i);
//比较最前航班的机票是否大于 max 的值,如果大于 max 的值,则重新对 max 赋值
if(atoi(lr_eval_string(cost)) < min){
min=atoi(lr_eval_string(cost));
lr_error_message("%d",max);
flagno=i;
}
}
sprintf(flightcost,"{WCSParam_Text1_%d}",flagno);
lr_save_string(lr_eval_string(flightcost),"flightnocost");
//通过标识位来确定航班
sprintf(flightelem,"{WCSParam_Text2_%d}",flagno);
lr_save_string(lr_eval_string(flightelem),"flightno");
web_submit_data("reservations.pl_3",
"Action=http://127.0.0.1:1080/WebTours/reservations.pl",
"Method=POST",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/reservations.pl",
"Snapshot=t24.inf",
"Mode=HTTP",
ITEMDATA,
"Name=outboundFlight", "Value={flightno};{flightnocost};10/26/2011", ENDITEM,
"Name=numPassengers", "Value=1", ENDITEM,
"Name=advanceDiscount", "Value=0", ENDITEM,
"Name=seatType", "Value=Coach", ENDITEM,
"Name=seatPref", "Value=None", ENDITEM,
"Name=reserveFlights.x", "Value=57", ENDITEM,
"Name=reserveFlights.y", "Value=13", ENDITEM,
LAST);
web_submit_data("reservations.pl_4",
"Action=http://127.0.0.1:1080/WebTours/reservations.pl",
"Method=POST",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/reservations.pl",
"Snapshot=t25.inf",
"Mode=HTTP",
ITEMDATA,
"Name=firstName", "Value=test", ENDITEM,
"Name=lastName", "Value=test", ENDITEM,
"Name=address1", "Value=", ENDITEM,
"Name=address2", "Value=", ENDITEM,
"Name=pass1", "Value=test test", ENDITEM,
"Name=creditCard", "Value=", ENDITEM,
"Name=expDate", "Value=", ENDITEM,
"Name=oldCCOption", "Value=", ENDITEM,
"Name=numPassengers", "Value=1", ENDITEM,
"Name=seatType", "Value=Coach", ENDITEM,
"Name=seatPref", "Value=None", ENDITEM,
"Name=outboundFlight", "Value={flightno};{flightnocost};10/26/2011", ENDITEM,
"Name=advanceDiscount", "Value=0", ENDITEM,
"Name=returnFlight", "Value=", ENDITEM,
"Name=JSFormSubmit", "Value=off", ENDITEM,
"Name=.cgifields", "Value=saveCC", ENDITEM,
"Name=buyFlights.x", "Value=41", ENDITEM,
"Name=buyFlights.y", "Value=17", ENDITEM,
LAST);
web_url("bookanother.gif",
"URL=http://127.0.0.1:1080/WebTours/images/bookanother.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/reservations.pl",
"Snapshot=t26.inf",
LAST);
return 0;
}


最后确定监控模型,即在测试过程中需要监控哪些信息,本实例的监控模型见表 16-9


关于“性能测试实例(二):构建”就学习到这里了,每个工作日小编都会更新一个小知识,希望大家多多关注我们,一起来学习喔!

、构建

设计阶段完成后,进入构建阶段,主要完成设计测试用例、开发测试脚本和确定监控模型。

首先设计测试用例,本实例中设计的测试用例包括登录和订票两个功能,登录的测试用例见表16-7


本文翻译自 http://doc.norang.ca/org-mode.html ,原文作者为Bernt Hansen 。由于原文较长,因此会分多篇文章来发布。转载请标记出处。

rg-mode, 用文本文件管理日常(二)

Org-mode, 用文本文件管理日常(一)


通过capture快速添加一个新任务



org capture mode取代了 remember mode 用来捕获任务以及备忘录。

为使得添加任务更加有效率,我定义了最少的capture模板,曾经我预定义了很多capture模板, 甚至每个org文件都定义一个模板。我通过绑定的快捷键 C-c c 来启动org-capture功能,然后 选择一个合适的模板,并且将捕获的内容写进合适的文件的 * Tasks 分类下面。

我发现我还得将这些capture的任务写入到不同的org文件的不同位置,因此定义这么多capture 模板根本没法帮助我。因此我修改了原来的工作流,使用最少的capture模板–这样我创建任务将会非常快 并且只要重新提取到特定文件一次。当然这样也节省了我维护我org-capture模板的时间,尤其当需要新加 org文件时候。


1.1 capture 模板

当需要添加一个新任务时候,我会将新任务归入如下几类模板中的一种:

  • 要回个电话(p)
  • 要开个会(m)
  • 要回个邮件(r)
  • 添加新任务(t)
  • 添加新备忘(n)
  • 突发事情(j)
  • 新兴趣(h)


然后再选择相应模板。

如下是我的capture 模板的配置:

#+header: :tangle yes
#+begin_src emacs-lisp
(setq org-directory "~/git/org")
(setq org-default-notes-file "~/git/org/refile.org")


;; I use C-c c to start capture mode
(global-set-key (kbd "C-c c") 'org-capture)


;; Capture templates for: TODO tasks, Notes, appointments, phone calls, meetings, and org-protocol
(setq org-capture-templates
      (quote (("t" "todo" entry (file "~/git/org/refile.org")
               "* TODO %?\n%U\n%a\n" :clock-in t :clock-resume t)
              ("r" "respond" entry (file "~/git/org/refile.org")
               "* NEXT Respond to %:from on %:subject\nSCHEDULED: %t\n%U\n%a\n" :clock-in t :clock-resume t :immediate-finish t)
              ("n" "note" entry (file "~/git/org/refile.org")
               "* %? :NOTE:\n%U\n%a\n" :clock-in t :clock-resume t)
              ("j" "Journal" entry (file+datetree "~/git/org/diary.org")
               "* %?\n%U\n" :clock-in t :clock-resume t)
              ("w" "org-protocol" entry (file "~/git/org/refile.org")
               "* TODO Review %c\n%U\n" :immediate-finish t)
              ("m" "Meeting" entry (file "~/git/org/refile.org")
               "* MEETING with %? :MEETING:\n%U" :clock-in t :clock-resume t)
              ("p" "Phone call" entry (file "~/git/org/refile.org")
               "* PHONE %? :PHONE:\n%U" :clock-in t :clock-resume t)
              ("h" "Habit" entry (file "~/git/org/refile.org")
               "* NEXT %?\n%U\n%a\nSCHEDULED: %(format-time-string \"%<<%Y-%m-%d %a .+1d/3d>>\")\n:PROPERTIES:\n:STYLE: habit\n:REPEAT_TO_STATE: NEXT\n:END:\n"))))
#+end_src

capture mode能够对新建任务自动处理计时与终止计时。该功能是capture自带功能并不需要新添加 代码来实现该功能。当我启动capture mode创建的任务,任务通过指定 :clock-in t 就可以自动开始计时,当任务通过快捷键 C-c C-c 将任务写入文件时候,会重新从原来计时的任务开始计时。

通过capture mode创建的那种快速完成的计时任务,当短时间结束时(通常少于1分钟) 会在我的任务里边 产生一个空的计时抽屉,而这个空的计时抽屉不是特别有用。因此我会将如下计时抽屉删除掉。

#+begin_src org :exports src
,* TODO New Capture Task
  :LOGBOOK:
  :END:
  [2010-05-08 Sat 13:53]
#+end_src


我使用如下自定义的函数来移除这些在 LOGBOOK 生成的空的计时日志。

#+header: :tangle yes
#+begin_src emacs-lisp
;; Remove empty LOGBOOK drawers on clock out
(defun bh/remove-empty-drawer-on-clock-out ()
  (interactive)
  (save-excursion
    (beginning-of-line 0)
    (org-remove-empty-drawer-at "LOGBOOK" (point))))


(add-hook 'org-clock-out-hook 'bh/remove-empty-drawer-on-clock-out 'append)


#+end_src


1.2 多个capture任务文件

我只使用一个org 文件来作为我capture模板的目标文件。

我会在这个命名为 refine.org 文件中存备忘,任务,回电以及org-protocol相关的任务. 曾经也用过多个文件,但是 发现多个文件并没有什么优势。

通常这个文件是空的,除了第一行创建了 REFILE 标签。

这个文件唯一的一行永久存在的内容如下:

#+begin_src org :exports src
#+FILETAGS: REFILE
#+end_src


1.3 capture任务需要快速创建

好了, 当我正在做些事情时候-我需要记住它。我不想停下手上的事。我可能在孵化一个项目, 通常我不希望脱离当前聚焦的事情,但是同时我又不想错过新来的的事情。

这时我该怎么办?通过 C-c c 启动capture mode然后选择 t ,然后我的buffer中会产 生一个如下格式的任务:

#+begin_src org :exports src
,* TODO 
  [2010-08-05 Thu 21:06]


  [[file:~/git/org-mode-doc/org-mode.org::*Capture%20Tasks%20is%20all%20about%20being%20FAST][Capture Tasks is all about being FAST]]
#+end_src

在新生成的TODO任务中写入新来的任务细节,然后 C-c C-c 这条任务就会写入refile.org文件。然后我就可以继续当前手上工作,同时之前任务也不会丢失,我此时也不必花精力去考虑新来的任务。

对于capture任务note更新时间会记录下来。capture模板自动设置计时以及停止计时。这对突然的 打断以及回电话非常有用。


2 重定向任务



重定向任务很简单。当我的refile.org文件中通过capture mode积累了一堆任务之后。我需要将这些任务移动到正确的org文件的合适分类中。我所有的在用的org文件都通过 org-agenda-files 变量定义,都可以在agenda中显示。

我有时候会在refile.org文件中累积长达1周的capture任务。在agenda 视图中显示出来的当日任务,我通常当天就重定向到其他文件中。我喜欢 保持重定向任务列表是空的。


2.1 重定向任务设置

为重定向任务到新的org文件中,你需要知道这些任务应该重定向到哪里。

我的设置中,会将 org-agenda-files 定义的文件以及当前文件作为重定向的目标。

我现在使用IDO来完成目标查找。我发现IDO处理起来比之前的设置更加快。刚开始我并不喜欢IDO, 但是当看了些相关的文档后,了解到 C-SPEC 可以限制目标搜搜数量,我发现它比我之前设置好 用多了。现在,当我想重定向refile文件中的任务时候,我只需要执行快捷键 C-c C-w 启动重定向流程, 然后输入一些匹配字段,然后执行 C-SPC 限制到匹配到当前列表,然后继续查找其他目标。 C-j 可以选择 当前的作为目标。我喜欢这样。我通常显示所有的目标文件大纲列表,因此我可以拥有相同的头在不同子树下 或者不同项目中,并且当重定向时也能够方便区分。

当前我会将 DONE 状态任务作为有效重定向任务。这样可以保证重定向目标合适大小。

我的重定向配置如下:

#+header: :tangle yes
#+begin_src emacs-lisp
; Targets include this file and any file contributing to the agenda - up to 9 levels deep
(setq org-refile-targets (quote ((nil :maxlevel . 9)
                                 (org-agenda-files :maxlevel . 9))))


; Use full outline paths for refile targets - we file directly with IDO
(setq org-refile-use-outline-path t)


; Targets complete directly with IDO
(setq org-outline-path-complete-in-steps nil)


; Allow refile to create parent tasks with confirmation
(setq org-refile-allow-creating-parent-nodes (quote confirm))


; Use IDO for both buffer and file completion and ido-everywhere to t
(setq org-completion-use-ido t)
(setq ido-everywhere t)
(setq ido-max-directory-size 100000)
(ido-mode (quote both))
; Use the current window when visiting files and buffers with ido
(setq ido-default-file-method 'selected-window)
(setq ido-default-buffer-method 'selected-window)
; Use the current window for indirect buffer display
(setq org-indirect-buffer-display 'current-window)


;;;; Refile settings
; Exclude DONE state tasks from refile targets
(defun bh/verify-refile-target ()
  "Exclude todo keywords with a done state from refile targets"
  (not (member (nth 2 (org-heading-components)) org-done-keywords)))


(setq org-refile-target-verify-function 'bh/verify-refile-target)
#+end_src

为了将任务重定向到 norang.org 文件,我通常将光标移到需要重定向的任务上,然后执行 C-c C-w ,然后执行 nor C-SPC sys RET 就可以完成了。IDO 补全模式让定位目标非常方便。


2.2 重定向任务

需要重定向的任务都在agenda的特定块中。为快速找到需要重定向的任务,我先通过 F12 SPC 打开 agenda视图,然后滚动到第二节: Tasks to Refile . 该视图中显示所有的任务(即使有些任务已经 被标记为=donw=状态)

在agenda中批量将任务重定向到相同位置也是非常方便。只要列出所有需要批量重定向任务,然后通过 m 标记这些任务,最后执行 B r 将会将所有标记的task重定向到新的目标。通常,我也会通过执行 C-2 C-c C-w 重定向当前任务作为当前计时任务的子任务.

执行重定向耗时通常在1分钟以内,因此我每天都会重定向好几次。


2.3 重定向备忘

我通常在大部分org 文件中保留 * Notes 类型。备忘通常通过capture模板创建,同时会 给任务加上 NOTE 标签,由于有标签,所以方便通过agenda在不同文件快速查找。

备忘首先会创建在 refile.org 文件中,然后通过重定向功能重定向到合适项目文件中。有些项目 相关备忘会重定向到相应的项目任务下面,而非 * NOTES 大纲中。这种备忘通常只和该项目相关, 通常项目结束后就没太大用处了–当归档项目时候,删除这些备忘也没什么关系。


2.4 重定向回电以及会议

回电以及会议任务也是通过capture mode来创建的。在通过capture mode 模板创建这种任务时 会启动计时,当回电完成或者会议结束,计时也会结束。

回电以及会议任务也是生成到 refile.org 中,然后通过重定向功能定向到合适的位置。有些是项目相关的回电,我们也希望在合适的分类中跟踪。我会重定向回电以及会议任务到合适 项目中,这样跟踪以及报告会更加精确。