ITPUB论坛-中国最专业的IT技术社区

 手机号登录  找回密码
 注册
查看: 9105|回复: 34

[原创] 做一个开发人员认可的测试人员(系列3)-也谈自动化测试框架[缓慢更新中]

[复制链接]
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-7-22 17:46 | 显示全部楼层 |阅读模式
本帖最后由 crazypeter2005 于 2013-7-22 17:46 编辑

这是第一个系列的文章中弄掉了的一篇。我答应大家有空补充起来的,这个会慢慢的更新下去,希望对大家有些帮助:

我做过自动化测试好多年,也用过不少的测试工具,对自动化的测试理论也了解不少,研究生的论文就是写的自动化测试。现在看来那个论文真是的很稚嫩。
为了更好的提高重用性,需要引入自动化测试框架,让测试能更节省人力,让测试更加可靠。

先介绍我接触的第一个自动化测试框架,是一个测试界面的框架:
被测产品是一个C/S结构的程序,采用Rational Robot来做产品的自动化。
由于Robot对某些窗口支持不好,测试框架还采用了SAS的自动化前辈Carl Nagle的部分代码
整个ProductA有76个窗口,有800个Case。
整个架构是这样子的:
Test Framework.jpg

我来简单介绍一下子:ProductA是被测试的产品,ProductB是有时候和ProductA做集成的产品。
第一层是底层:包括Common Function以及产品给到的Property文件或者XML文件,这里面只有Product Specific List View Utilities和Product Related Properties File是和被测产品相关的,是为产品而定制的或者是被测产品开发的人提供的文件。

第二层是中间层:按照窗口来划分,每个Checkbook,每个EditBox,每个RadioButton都是一个独立的Item,每个Item都封装一个方法,我们叫Task。不同窗口的OK Button的点击被封装到不同的窗体的文件中,作为各自独立的Task。Task有时间需要调用Common Function里面读取Property文件的方法,总的来说,Task和被测产品关系不算大。
      中间层还有一个叫做Wizard,就是调用一系列的Task,每个Wizard都是和被测产品的业务相关系的。
      如何区分Task和Wizard我举个例子:
      QQ登陆界面:
      点击UserNmae,输入UserName,清空UserName,点击密码,输入密码,清空密码,点击登陆 这就是7个Task。
      而一个Wizard就是
      1 输入用户名,2 输入密码, 3 点击登陆,这就是一个Wizard。Wizard不做验证,只是调用各个小的Task。Wizard调用后验证的结果在TaskCase里面调用。

第三层是TestCase,调用Wizard,然后用Common Function的Verify系列方法去验证,Pass Or  Not。

分为三层架构后,有有点也有缺点:
      优点是:1 对产品进行了隔离,一般Case写好后就不需要修改,只是去调整底层的Task和Wizard。
                     想想Case有800个,而Task和Wizard写在一起才76个文件,每个窗体一个。
                  2 Task和Wizard起名有规范,直接读Case能够读懂,写Case的工作交给只会手工的人也能写,调试交给专职自动化的人来。
                  3 产品小规模调整的时候,只需要调整Task,甚至不动Wizard和Task。
                  4 便于做本地化,只修改properties文件就行。

      缺点也是蛮明显的:1 产品不能大改动,界面如果是可配置的,我们就必须固定某种配置或者并行集中不同界面的配置,增加了复杂度。
                                 2 先期写Common Function和Wizard比较耗时。
                                 3 不熟悉Common Function的人调试得花费较长时间。

举简单的代码的例子吧。
1 Properties的例子
  1. STRINGTABLE DISCARDABLE
  2. BEGIN
  3.     IDS_DIALOGS_TITLE_MessageDlg      "Message"
  4.     IDS_DIALOGS_TITLE_WarningDlg      "Warning"
  5.     IDS_DIALOGS_TITLE_ErrorDlg        "Error"
  6.     IDS_DIALOGS_TITLE_DirPickerDlg    "Select a Folder:"
  7.     IDS_DIALOGS_TITLE_SelectDocuments "Select Documents"
  8. END
复制代码

2 Task 的例子
  1. ' Tasks:

  2. ' [All]
  3. Declare Sub APSrch_SelectView                  basiclib "A_PowerSearch" (ViewName as String)
  4. Declare Sub APSrch_ClickClearALL               basiclib "A_PowerSearch" ()
  5. Declare Sub APSrch_ClickSave                   basiclib "A_PowerSearch" ()
  6. Declare Sub APSrch_ClickLoad                   basiclib "A_PowerSearch" ()
  7. Declare Sub APSrch_ClickSearch                 basiclib "A_PowerSearch" ()
  8. Declare Sub APSrch_ClickDone                   basiclib "A_PowerSearch" ()
复制代码
  1. '############################################################################
  2. '#  Sub APSrch_SelectView((ViewName as String)
  3. '# DESCRIPTION:
  4. '#      [Task] Select view in power search.
  5. '# EXAMPLES:
  6. '#      Call APSrch_SelectView("General")
  7. '# Orig Author: XX
  8. '# Orig   Date: April 6, 2006   
  9. '# History:
  10. '#      April 5, 2006    Original Release      
  11. '############################################################################
  12. Sub APSrch_SelectView(ViewName as String)
  13.     Dim cmbName As String
  14.     cmbName=A_GetKeyRC("IDS_DESKTOP_LABEL_SelectView")
  15.     Call A_CmbSelect(cmbName, ViewName)
  16. End Sub
复制代码
3 Wizard的例子
  1. ' Wizards:

  2. Declare Sub APSrch_SearchByProperty            basiclib "A_PowerSearch" (Property as String, Filter as String, Value as String)
  3. Declare Sub APSrch_SerachByDocContent          basiclib "A_PowerSearch" (words() as String, condition() as String)
  4. Declare Sub APSrch_SearchByDate                basiclib "A_PowerSearch" (Property as String, Filter as String, DYear as String, Aonth as String, DDay as String)
复制代码
  1. '############################################################################
  2. '#  Sub APSrch_SearchByProperty(Property as String,Filter as String,Value as String)
  3. '# DESCRIPTION:
  4. '#      Search by  a property  in general tab
  5. '# PARAMETERS:
  6. '#      [In]    Property e.g., "DocumentName"
  7. '#      [In]    Filter   e.g., "Contain"
  8. '#      [In]    Value    e.g., "Test"
  9. '# ERRORS:
  10. '#      (none)
  11. '# EXAMPLES:
  12. '#      Call APSrch_SearchByProperty("DocumentName","Contain","Test")
  13. '#      APSrch_SearchByProperty"DocumentName","Contain","Test"
  14. '# Orig Author: XX
  15. '# Orig   Date: May 25, 2005
  16. '# History:
  17. '#      May 25, 2005    Original Release
  18. '#      April 11, 2006 XX Modified "APSrch_General_ClickAdd" instead of "APSrch_ClickAdd" and
  19. '#                            "APSrch_General_InputValue" instead of "APSrch_InputValue"
  20. '############################################################################
  21. Sub APSrch_SearchByProperty(Property as String,Filter as String,Value as String)
  22.     A_Focus A_GetKeyRC("CPN_DESKTOP")
  23.     Call ADesk_ClickTopPowerSearch()
  24.    
  25.     A_Focus A_GetKeyRC("IDD_POWERSEARCH_TEMPLATE.CAPTION")
  26.     APSrch_SelectProperty Property
  27.     APSrch_SelectFilter Filter
  28.     APSrch_General_InputValue Value
  29.    
  30.     Call APSrch_General_ClickAdd()
  31.     APSrch_ClickSearch
  32.    
  33.     Call A_Delay(10000)
  34. End Sub
复制代码
4 Test Case例子
  1. '############################################################################
  2. '# Test if we can create a document and later search out
  3. '# DESCRIPTION:
  4. '#
  5. '#

  6. '#
  7. '############################################################################
  8. Sub Test_Search_By_Properties(Property as String,Filter as String,Value as String)

  9. Call User_Login(NormalUser1, Password1)
  10. Call A_CreateDoc(TestPath1, DocName)
  11. Call A_Desktop_Focus()
  12. Call A_Desk_ClickTopPowerSearch()

  13. Call A_PowerSearch_Focus()
  14. Call APSrch_SelectProperty Property
  15. Call APSrch_SelectFilter Filter
  16. Call APSrch_General_InputValue Value

  17. Call APSrch_General_ClickAdd()
  18. Call APSrch_ClickSearch()

  19. Call A_Delay(10000)

  20. Dim Count As Interger
  21. Call Count = CommonUtil_GetListView_Count()
  22. If Count = 1 then
  23.         Test_Log("Test_Search_By_Properties","Pass",CommonUtil_GetCurrenTimeStamp)
  24. Else
  25.         Test_Log("Test_Search_By_Properties","Fail",CommonUtil_GetCurrenTimeStamp)
  26. End If

  27. Call A_DeleteFileAll()

  28. Call A_Logout()
  29. End Sub
复制代码
可以看到整个Case是可读可维护的,维护的成本也是比较小的。有编程基础的人通过写1-2个Case就能熟悉框架,通过Debug几次就能迅速定位错误。
这是接触到的第一个自动化的框架...
(后续缓慢更新中...)
认证徽章
论坛徽章:
1009
紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:48:45紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-10-15 14:45:48
发表于 2013-7-23 14:36 | 显示全部楼层
强势插入前排~~

使用道具 举报

回复
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-7-23 16:37 | 显示全部楼层
  今天继续更新:
  这个框架中我个人用的最多的函数就是Focus方法以及衍生方法,这个方法没有发现有啥跑不过的情况:
  1. '############################################################################
  2. '#  Function A_Focus(strCaption as String) as Integer
  3. '# DESCRIPTION:
  4. '#      Focus object
  5. '# PARAMETERS:
  6. '#      [In]    strCaption e.g.,"CPN_DESKTOP"
  7. '# RETURNS:
  8. '#      true success
  9. '#      false fail   
  10. '# ERRORS:
  11. '#       (none)
  12. '# EXAMPLES:
  13. '#      return = A_Focus("CPN_DESKTOP")
  14. '# Orig Author: XX)
  15. '# Orig   Date: May 09, 2005
  16. '# History:
  17. '#      May 09, 2005    Original Release
  18. '#      Apr 17, 2006    XX    If the window state is minimized, then restore the position.
  19. '############################################################################
  20. Function A_Focus(strCaption as String) as Integer
  21.     Dim Result as Integer
  22.     Result=SQAWaitForObject("\;type=window;Caption=" &strCaption, 20000)
  23.     If Result<> sqaSuccess Then
  24.        A_Focus= false      
  25.        Exit Function
  26.     Else
  27.        ' If the window state is minimized, then restore it.
  28.         Dim state As String

  29.         result = SQAGetProperty("\;type=window;Caption=" &strCaption, "WindowState", state)
  30.         ' Valid WindowState:
  31.         ' 0 : Normal
  32.         ' 1 : Minimized
  33.         ' 2 : Maximized
  34.         If (state = "1") Then   
  35.             Window SetContext, "Caption=" &strCaption , ""
  36.             Window RestorePos, "", ""   
  37.         End If

  38.         A_Focus= true   
  39.         Window SetContext, "Caption=" &strCaption , ""
  40.     End If   
  41. End Function
复制代码
  1. '############################################################################
  2. '#  Function A_WaitWindowAppear(caption As String, Optional Timeout As Variant)  As Integer
  3. '# DESCRIPTION:
  4. '#      Wait for the object to appear
  5. '# PARAMETERS:
  6. '#      [In]    caption      caption title
  7. '#      [In]    Timeout      time out
  8. '#  
  9. '# EXAMPLES:
  10. '#      result = A_WaitWindowAppear("Type=CheckBox;WindowIndex=19")
  11. '# Orig Author: XX
  12. '# Orig   Date: Apr 05, 2006
  13. '# History:
  14. '#      June 08, 2005    Original Release
  15. '############################################################################
  16. Function A_WaitWindowAppear(caption As String,Optional Timeout As Variant) As Integer
  17.         
  18.     If A_WaitForObject("\;Type=window;Caption=" & caption,Timeout) = sqaSuccess Then
  19.          A_WaitWindowAppear = True
  20.          Window SetContext, "Caption=" &caption , ""
  21.          Exit Function
  22.     Else
  23.          A_WaitWindowAppear = false
  24.          SQALogMessage sqaWarning, "Caption [" & caption & "] has not appeared" , ""
  25.     End If      

  26. End Function
复制代码
  1. '############################################################################
  2. '#  Function A_WaitWindowDisappear(recMethod As String, Optional Timeout As Variant)  As Integer
  3. '# DESCRIPTION:
  4. '#      Wait for the Window to disappear
  5. '#
  6. '# EXAMPLES:
  7. '#      result = A_WaitWindowDisappear("Type=CheckBox;WindowIndex=19")
  8. '# Orig Author: XX
  9. '# Orig   Date: June 08, 2005
  10. '# History:
  11. '#      June 08, 2005    Original Release
  12. '############################################################################
  13. Function A_WaitWindowDisappear(recMethod As String, Optional Timeout As Variant)  As Integer
  14.     A_WaitWindowDisappear = A_WaitObjectDisappear("\;type=window;Caption=" & recMethod, Timeout)
  15. End Function
复制代码


在各种最小化或者刚打开的窗口都能Focus住并且最大化。

第二个问题就是命名不可避免会有重复和多态出现。有时候还是得去扫一眼代码,看看不同的Wizard到底是做啥的。
比如导入文档的时候可选的不同字段,这个时候就会出现多态,也就是同一个类似的名字,但是不同的参数。
  1. Declare Function ADoc_AddPolicy        basiclib "A_Document" (Title As String, FileName As String, _
复制代码
  1. Declare Function ADoc_AddPolicy1       basiclib "A_Document" (Title As String, _
  2.                                                                 FileName As String, _
  3.                                                                 Optional ExpireDate As Variant, _
  4.                                                                 Optional Country As Variant, _
  5.                                                                 Optional City As Variant, _
  6.                                                                 Optional Desc As Variant, _
  7.                                                                 Optional StateName As Variant, _
  8.                                                                 Optional Template As Variant, _
  9.                                                                 Optional TstTimeStamp As Variant, _
  10.                                                                 Optional TstTime As Variant) As Integer
复制代码
如果在输入文档的时候需要输入过期时间等字段就只能选择第二个函数,第一个函数是最基础的只去填写必填字段的。
这个时候就是考察队Test Framework是否熟悉了。

最后一个要说的问题就是在Test Case 的每个部分命名的问题,为了隔离每个Case,让每个Case都能独立于其他Case,让Case粒度更小。
我们这样约定:
1 第一部分是创建环境,比如为了验证Search文档,第一步我得Create/Import文档。
2 第二部分验证Search文档。
3 第三部分是删除掉第一步的文档。

我们有2个命名方法
第一种: PreEnv_CreateDoc
              Verify_SearchDoc
              PostEnv_DeleteDoc

第二种: Part1
             Part2
             Part3

  我想你一定和当初的我一样毫不犹豫选择了第一种。呵呵。
  想想如果第一步不是创建一个文档这样简单,而是要做10几个无法简写的步骤呢?于是我们看到了各种各样千奇百怪的命名,在Debug的时候,调试的人想死的心都有了。
  于是我们简化到第二种,反正第一步就是第三步的逆操作。第二步就是验证。

  稳定,简单压倒一切。为了简单可维护。有时候得牺牲掉可读性。

  另外就是各位写Test Framework的大大,也得去学学算法。曾经有个人写了个在某个负责的C/S的复杂的类似于Excel那样的10X10的格子取到第三行第四列的显示的值。
  这个函数单独运行需要15秒。就是采取了较差的算法。
  改进后,大概100X100的取某个Cell的值只需要4秒。

  自动化框架也是和Case一样的与时俱进,维护Case的人和维护框架的人最好能有交互,这样写框架的能了解测试的逻辑和被测产品,而写Case的人能了解更多框架的事情。 自动化重来都不是一个简单的事情,需要在先期投入大量的人力物力和时间。

  下一个我会写一下我接触到的一个Java写的测试API的测试框架,希望大家能受益。

使用道具 举报

回复
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-8-5 11:27 | 显示全部楼层
      到自动化测试框架的第二部分我就介绍下一个Java写的自动化测试框架。这个框架的核心比较小,所应用的位置也比较有局限性。这个框架测试的不是功能,而是性能。用一句话来描叙,这个框架是用Java调用被测试产品的Java API的性能,本身也是Java编写的。
      之所以不用Load Runner或者别的工具是因为测试团队的大部分人都是开发团队成员,对商用测试工具的价格敬而远之,本身也不都是搞测试出身的。商量了很久,还是决定自己写测试API的框架。
      以下是这个框架的结构图:
       QTool.jpg
      图中黄色的部分就是产品相关的API,是不属于测试框架的部分,而白色的部分就是整个框架的组成部分。
      先撇开被测产品的API不谈,整个工具暂且称作QTool。主要组成部分是Common,DB,Report以及User Exit4个大部分,另外还有关于版本以及Launcher部分。
其中Launcher是启动QTool的部分,Common是定义TestCase以及Test Scenario以及定义Log以及Util和VU以及CommandClient的部分,是整个框架的核心部分。DB是调用DB相关的Result的部分。Report部分详细定义了Test Report的格式以及最终画图的部分。而UserExit部分则是定义了直接调用被测产品API的接口函数。
      再来看看被测产品的API,主要包括2部分:
      1 基本的CRUD部分;
      2 最基本的BPM的3个API。

      先来看看我们如何定义性能测试:
      不是说简单的应用LoadRunner把被测产品压CPU到多少就是性能测试了。这里这种简单粗暴的方式至少有2个错误:
      1 就拿我这里简化的API来说:至少有Create、Update、Retrieve和Delete,以及3个BPM的API。你的简单的压CPU是否把所有的API都包含进去了,我的建议是可以
重复但是不能遗漏。
      2 姑且算你的这种压制算是无差别攻击吧。但是你最好保证每一次Loop都是射中目标的,这样打个比方吧。你每次Create需要真的导入一个文档才算真的压到被测产品了,不然你随便录一个,都没有真正存进去文档,被测系统只是再不断报错或者只是在错误恢复。这样就不算有效。

      所以真的性能测试,必须你保证你的每一发子弹都命中目标,然后覆盖所有被测的API。然后再加大VU以及Loop,这样才算是完整的压力测试。


      为了简单起见,我们假设如下的API的速度相同的:
      假设我们定义这样1个Case:
      用户登录产品-Create-Update-Update-DocRoute_Start-DocRoute_Continue-DocRoute_Finish-Delete-用户退出产品
      可以预见1个VU跑完一个Loop是没有问题的,但是如果2个VU跑的话,必须做好隔离机制,因为可能第二个用户还在用第一个用户跑到Delete已经删除的文档。

       QTool有一个理念和别的性能测试有点不一样。
       我们引入了吞吐量的概念。就是只有做成功的才算做吞吐量。因为随着压力逐步增大,系统的反应是越来越慢,响应时间越来越大,到某个极限后,你请求100个请求但是系统能反应到的只有80,剩下的20个会超时而算作错误,这个时候的吞吐量就不能算成100,而是80.
       Snap 2013-08-05 at 10.42.31.png
      如上图所示,在一个VU的时候,我们让一个用户跑1个小时如下流程算Saniety Test,这个时间我们算作Baseline。
      用户登录产品-Create-Update-Update-DocRoute_Start-DocRoute_Continue-DocRoute_Finish-Delete-用户退出产品
      可以看到随着横轴系统压力增大,响应时间纵轴在增大,而系统资源耗损也在变大。
      我们把2倍响应时间算成持久性测试,需要跑连续72小时,此时系统资源耗费约为50%。
      我们把5倍相应时间算成压力测试,需要跑连续8小时,此时资源耗费无限接近100%。这个不是绝对,有可能出现瓶颈,比如受制于磁盘或者内存,达不到100%。


      如何计算出响应时间或者如何知道成功的次数呢?我们来稍稍看下代码:
     
  1. try {
  2.                 Class.forName("Name Of JDBC").newInstance();
  3.                 Connection conn = DriverManager.getConnection(url, userid, password);
  4.                 Log.getInstance().trace("Connected to resultDB.");
  5.                 
  6.                 Statement ps = null;
  7.                         ResultSet rs = null;
  8.                         ps = conn.createStatement();
  9.                         String sqlcmd="";
  10.                         SimpleDateFormat df = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.US);
  11.                        
  12.                         report.append("\n\n\n============================Final Report==================================\n\n\n");
  13.                        
  14.                         int test_no = 0;
  15.                         sqlcmd = "SELECT test_no,min(test_start) start, max(test_end) end, max(levelload_start) lv_start, min(levelload_end) lv_end," +
  16.                                         "b.name, sum(vu) vu_num, a.driver_interval FROM EXECUTION a,Scenarios b, environment c where a.sc_id=b.sc_id and tester='" + Config.getInstance().tester.trim() +"' and " +
  17.                                         "a.env_id=c.env_id and c.description='"+Config.getInstance().test_env_desc.trim()+"'  group by test_no,b.name,a.driver_interval order by test_no desc fetch first 1 rows only";

  18.                         rs = ps.executeQuery(sqlcmd);
  19.                         if (rs!=null && rs.next())
  20.                                 {
  21.                                    test_no = rs.getInt(1);
  22.                                    test_Start=rs.getTimestamp(2);
  23.                                    test_End=rs.getTimestamp(3);
  24.                                    levelload_Start = rs.getTimestamp(4);
  25.                                    levelload_End = rs.getTimestamp(5);
  26.                                    driver_interval = rs.getInt(8);
  27.                                   
  28.                                   
  29.                                    report.append("----> test started: " + df.format(test_Start)+ " GMT <----\n");
  30.                                    report.append("----> test stopped: " + df.format(test_End)+ " GMT <----\n");
  31.                                    report.append("----> VU: " + rs.getInt(7)
  32.                                                         + " <----\n");
  33.                                   
  34.                                    report.append("----> Duration: " + (test_End.getTime()-test_Start.getTime()) + " millisecond <----\n");
  35.                                    report.append("----> Scenario: " + rs.getString(6)
  36.                                                         + " <----\n");
  37.                                   
  38.                                 }
  39.                         else {
  40.                                 System.out.println("Could not find relative execution record by info in qaToolConfig.properties.\n");
  41.                                 System.exit(0);
  42.                         }
  43.                        
  44.                         //Get Case info
  45.                         sqlcmd = "select a.case_id,a.case_name,a.description from caseinfo a, execution b where a.exec_id=b.exec_id and b.test_no=" + test_no +" order by a.case_id";
  46.                        
  47.                         rs = ps.executeQuery(sqlcmd);
  48.                                
  49.                         while(rs.next()) {
  50.                                 caseID_List.add(rs.getString(1));
  51.                                 caseName_List.add(rs.getString(2)+":"+rs.getString(3));
  52.                         }
  53.                        
  54.                         report.append("----> Test Case Num: " + caseID_List.size() + " <----\n");
  55.                         report.append("----> the whole performance statistics <----\n");
  56.                        
  57.                 sqlcmd = "select res.case_id, trunc(timestampdiff(2,char(res.ts - exec.ts_start))/exec.driver_interval, 0)+1 seq, sum(loop_count) l_cnt, " +
  58.                      "avg(avg) l_avg, min(min) l_min, max(max) l_max, sum(error_count) err_cnt, sum(exe_count) exe_cnt, sum(total_sleeptime) slp " +
  59.                      "from(select a.test_start +( (b.interval_seq-1)*a.driver_interval) seconds ts,b.case_id,b.interval_seq " +
  60.                      "from execution a, tresult b where a.exec_id=b.exec_id and a.test_no="+test_no+" ) res," +
  61.                      "(SELECT test_no, min(test_start) ts_start, max(test_end) ts_end, driver_interval FROM EXECUTION " +
  62.                      "where test_no=" + test_no+"  group by test_no,driver_interval) exec," +
  63.                      "tresult where res.case_id = tresult.case_id and res.interval_seq=tresult.interval_seq " +
  64.                      "group by res.case_id, trunc(timestampdiff(2,char(res.ts - exec.ts_start))/exec.driver_interval, 0) ";
  65.                 
  66.                 //System.out.println(sqlcmd);
  67.                 rs = ps.executeQuery(sqlcmd);
  68.                 int case_id = -1;
  69.                 
  70.                 while(rs.next()) {
  71.                        
  72.                         int temp_id = rs.getInt(1);
  73.                         if (temp_id != case_id)
  74.                         {
  75.                                 case_id = temp_id;
  76.                                 int i=0;
  77.                                 for(;i<caseID_List.size();i++)
  78.                                 {
  79.                                         if(Integer.parseInt(caseID_List.get(i).toString()) == case_id)
  80.                                                 break;
  81.                                 }
  82.                                
  83.                                 //A new line,
  84.                                 report.append("#=== "+ caseName_List.get(i) + " ========\n#Interval_Seq \tLoop_Cnt \tAvg \tMin \tMax \tSleep \tErr_Cnt \tExe_Cnt \n");
  85.                         }
  86.                        
  87.                         report.append(rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString(4)
  88.                                         + "\t" + rs.getString(5)+ "\t" + rs.getString(6)+ "\t" + rs.getString(9) +"\t"+ rs.getString(7)
  89.                                         + "\t" + rs.getString(8) +"\n");
  90.                 }
  91.                 
  92.                 //level load
  93.                 long lvstart_Seq = (levelload_Start.getTime()-test_Start.getTime())/(driver_interval*1000) + 1;
  94.                 long lvstop_Seq = 0;
  95.                 if((levelload_End.getTime()-test_Start.getTime()) < driver_interval*1000)
  96.                 {
  97.                         lvstop_Seq = (levelload_End.getTime()-test_Start.getTime())/(driver_interval*1000) + 1;
  98.                 }
  99.                 else {
  100.                         lvstop_Seq = (levelload_End.getTime()-test_Start.getTime())/(driver_interval*1000);
  101.                 }
  102.                 report.append("----> performance statistics for level load <----\n");
  103.                 report.append("----> level load started in interval " + lvstart_Seq
  104.                                         + " , Date: " + df.format(levelload_Start) + " GMT <----\n");
  105.                         report.append("----> level load stopped in interval " + lvstop_Seq
  106.                                                 + " , Date: " + df.format(levelload_End) + " GMT <----\n");
  107.                        
  108.                        
  109.                         report
  110.                                         .append(" ========\n#CaseName \t Description \tLoop_Cnt \tRspTime \tavgRspTimePerExe \tErr_Cnt \tExe_Cnt \n");

  111.                 //merge
  112.                         String condition="";
  113.                         if(Config.getInstance().noReportForEmptyDes) {
  114.                                 condition = " and length(caseinfo.description)>0 ";
  115.                         }
  116.                         if (Config.getInstance().mergeResult)
  117.                         {

  118.                                
  119.                         sqlcmd = "select caseinfo.case_name,caseinfo.description, sum(l_cnt) lp_cnt," +
  120.                         "sum(l_avg*l_cnt) total_resp,  sum(err_cnt) , sum(exe_cnt) , sum(slp) from (" +
  121.                                         "select res.case_id, trunc(timestampdiff(2,char(res.ts - exec.ts_start))/exec.driver_interval, 0)+1 seq, sum(loop_count) l_cnt, " +
  122.                 "avg(avg) l_avg, sum(error_count) err_cnt, sum(exe_count) exe_cnt, sum(total_sleeptime) slp " +
  123.                 "from(select a.test_start +( (b.interval_seq-1)*a.driver_interval) seconds ts,b.case_id,b.interval_seq " +
  124.                 "from execution a, tresult b where a.exec_id=b.exec_id and a.test_no="+test_no+" ) res," +
  125.                 "(SELECT test_no, min(test_start) ts_start, max(test_end) ts_end, driver_interval FROM EXECUTION " +
  126.                 "where test_no=" + test_no+"  group by test_no,driver_interval) exec," +
  127.                 "tresult where res.case_id = tresult.case_id and res.interval_seq=tresult.interval_seq " +
  128.                 "group by res.case_id, trunc(timestampdiff(2,char(res.ts - exec.ts_start))/exec.driver_interval, 0)) result," +
  129.                 "caseinfo where caseinfo.case_id=result.case_id and seq between " + lvstart_Seq +" and "+ lvstop_Seq+
  130.                 condition +
  131.                 " group by caseinfo.case_name,caseinfo.description";
  132.      
  133.                         }
  134.                         else {

  135.                                 sqlcmd = "select caseinfo.case_name,caseinfo.description, sum(l_cnt) lp_cnt," +
  136.                                 "sum(l_avg*l_cnt) total_resp,  sum(err_cnt) , sum(exe_cnt) , sum(slp), caseinfo.case_id from (" +
  137.                                                 "select res.case_id, trunc(timestampdiff(2,char(res.ts - exec.ts_start))/exec.driver_interval, 0)+1 seq, sum(loop_count) l_cnt, " +
  138.                         "avg(avg) l_avg, sum(error_count) err_cnt, sum(exe_count) exe_cnt, sum(total_sleeptime) slp " +
  139.                         "from(select a.test_start +( (b.interval_seq-1)*a.driver_interval) seconds ts,b.case_id,b.interval_seq " +
  140.                         "from execution a, tresult b where a.exec_id=b.exec_id and a.test_no="+test_no+" ) res," +
  141.                         "(SELECT test_no, min(test_start) ts_start, max(test_end) ts_end, driver_interval FROM EXECUTION " +
  142.                         "where test_no=" + test_no+"  group by test_no,driver_interval) exec," +
  143.                         "tresult where res.case_id = tresult.case_id and res.interval_seq=tresult.interval_seq " +
  144.                         "group by res.case_id, trunc(timestampdiff(2,char(res.ts - exec.ts_start))/exec.driver_interval, 0)) result," +
  145.                         "caseinfo where caseinfo.case_id=result.case_id and seq between " + lvstart_Seq +" and "+ lvstop_Seq+
  146.                         condition +
  147.                         " group by caseinfo.case_id,caseinfo.case_name,caseinfo.description";
  148.                         }
  149.                         //System.out.println(sqlcmd);
  150.                 rs = ps.executeQuery(sqlcmd);
  151.                 while(rs.next())
  152.                 {
  153.                         report.append(rs.getString(1)+","+rs.getString(2)+","+rs.getString(3)+","+
  154.                                         rs.getInt(4)/rs.getInt(3)+","+rs.getInt(4)/rs.getInt(6)+","+rs.getString(5)+","+
  155.                                         rs.getString(6)+"\n");
  156.                 }
  157.                                 
  158.                        
  159.                        
  160.             } catch (Exception e) {
  161.                     e.printStackTrace();
  162.             }
  163.                
  164.                 return report;
  165.         }
  166.         public static void storeResultfromFile(String fileName) throws IOException
  167.         {
  168.                 FileReader fr = new FileReader(fileName);  //perf.log
  169.                 BufferedReader br = new BufferedReader(fr);
  170.                
  171.                 String line = br.readLine();
  172.                 ArrayList dataList = new ArrayList();
  173.                 boolean found = false;
  174.                 while (line != null) {
  175.                         if (line.indexOf("Final Report")<0 && (!found))
  176.                            {
  177.                                 line = br.readLine();
  178.                                 continue;
  179.                            }
  180.                         else {
  181.                                 found = true;
  182.                         }
  183.                         dataList.add(line);
  184.                         line = br.readLine();
  185.                 }
  186.                
  187.                 //System.out.println(dataList);
  188.                 String temp = new String();
  189.                
  190.                 //Get test start time
  191.                 temp = dataList.get(3).toString();
  192.                 String testStart_timestamp = temp.substring(20, 40);
  193.                
  194.                 //Get test stop time
  195.                 temp = dataList.get(4).toString();
  196.                 String testStop_timestamp = temp.substring(20, 40);
  197.                
  198.                 //VU
  199.                 temp = dataList.get(5).toString();
  200.                 int vu = Integer.parseInt(temp.substring(10, temp.lastIndexOf(" ")));
  201.                
  202.                 //Scenario name
  203.                 temp = dataList.get(7).toString();
  204.                 String sc_Name = temp.substring(15, temp.lastIndexOf(" "));
  205.                
  206.                 //testcase number
  207.                 temp = dataList.get(8).toString();
  208.                 int tc_num = Integer.parseInt(temp.substring(21, temp.lastIndexOf(" ")));
  209.                
  210.         ArrayList testcases = new ArrayList();
  211.         
  212.                
  213.                 //parse data
  214.                 int i = 10;
  215.                 for (i =10; i< dataList.size(); i++) {
  216.                         temp = dataList.get(i).toString();
  217.                        
  218.                         while(temp.indexOf("#Interval_Seq")>=0)
  219.                         {
  220.                                 ArrayList tc = new ArrayList();
  221.                                 tc.add(dataList.get(i-1));  //tc description
  222.                                 for (; i < dataList.size();i++)
  223.                                 {
  224.                                         temp = dataList.get(i).toString();
  225.                                         if(temp.indexOf("#===")>=0 || temp.indexOf("---->")>=0)
  226.                                                 break;
  227.                                         tc.add(temp);
  228.                                 }
  229.                                 testcases.add(tc);
  230.                         }
  231.                        
  232.                         if(temp.indexOf("---->")>=0)
  233.                                 break;                       
  234.                 }
  235.                
  236.                 //Get level load start time
  237.                 temp = dataList.get(i+1).toString();
  238.                 String levelloadStart_timestamp = temp.substring(temp.indexOf("Date:")+6, temp.lastIndexOf(" GMT"));
  239.                
  240.                 //Get level load start time
  241.                 temp = dataList.get(i+2).toString();
  242.                 String levelloadEnd_timestamp = temp.substring(temp.indexOf("Date:")+6, temp.lastIndexOf(" GMT"));
  243.                
  244.                
  245.                
  246.                 //System.out.println(levelloadStart_timestamp);
  247.                
  248.                 String url = Config.getInstance().resultDBURL;
  249.             String userid = Config.getInstance().resultDBUser;
  250.             String password = Config.getInstance().resultDBPassword;
  251.             
  252.             int execId = 0;
  253.             int test_no = 0;
  254.             int caseId = 0;
  255.             int buildId = 0;
  256.             int scID = 0;
  257.             int envID = 0;
  258.             int machine_ID = 0;
  259.             
  260.             String tester = Config.getInstance().tester;
  261.             String env_description=Config.getInstance().test_env_desc;
  262.             String build_info=Config.getInstance().test_buildinfo;
  263.             String test_desc=Config.getInstance().test_desc;
  264.             
  265.                         
  266.             try {
  267.                 Class.forName("Name Of JDBC").newInstance();
  268.                 Connection conn = DriverManager.getConnection(url, userid, password);
  269.                 Log.getInstance().trace("Connected to resultDB.");
  270.                 
  271.                 
  272.                 conn.setAutoCommit(false);
  273.                 Statement ps = null;
  274.                         ResultSet rs = null;
  275.                         ps = conn.createStatement();
  276.                        
  277.                         //get build_id
  278.                         ps.execute("lock table buildinfo in exclusive mode");
  279.                         rs = ps.executeQuery("select build_id from buildinfo where release='"+build_info+"'");
  280.                         if (rs!=null && rs.next())
  281.                                 buildId = rs.getInt(1);
  282.                         else //Insert a new scenario
  283.                         {
  284.                                 ps.executeUpdate("insert into buildinfo(release) values('"+build_info+"')");
  285.                                
  286.                                 rs = ps.executeQuery("select build_id from buildinfo where release='"+build_info+"'");
  287.                                 if (rs!=null && rs.next())
  288.                                         buildId = rs.getInt(1);
  289.                         }
  290.                         conn.commit();
  291.                        
  292.                 //get scenario_id by script name(.xml)
  293.                         ps.execute("lock table scenarios in exclusive mode");
  294.                         rs = ps.executeQuery("select sc_id from scenarios where name='"+sc_Name.trim()+"'");
  295.                         if (rs!=null && rs.next())
  296.                                 scID = rs.getInt(1);
  297.                         else //Insert a new scenario
  298.                         {
  299.                                 ps.executeUpdate("insert into scenarios(name) values('"+sc_Name.trim()+"')");
  300.                                
  301.                                 rs = ps.executeQuery("SELECT sc_id from scenarios where name='"+sc_Name.trim()+"'");
  302.                                 if (rs!=null && rs.next())
  303.                                         scID = rs.getInt(1);
  304.                         }
  305.                         conn.commit();
  306.                        
  307.                         //get env_id by env_description
  308.                         InetAddress inet = InetAddress.getLocalHost();
  309.                         String hostName = inet.getCanonicalHostName();
  310.                        
  311.                         ps.execute("lock table environment in exclusive mode");
  312.                         rs = ps.executeQuery("select env_id from environment where description='"+env_description+"'");
  313.                         if (rs!=null && rs.next())
  314.                                 envID = rs.getInt(1);
  315.                         else {
  316.                                 ps.executeUpdate("insert into environment(description) values('"+env_description+"')");
  317.                                
  318.                                
  319.                                 rs = ps.executeQuery("select env_id from environment where description='"+env_description+"'");
  320.                                 if (rs!=null && rs.next())
  321.                                         envID = rs.getInt(1);
  322.                         }
  323.                         conn.commit();
  324.                        
  325.                         ps.execute("lock table machine in exclusive mode");
  326.                         rs = ps.executeQuery("select b.machine_id from machine b where b.hostname='"+hostName+"'");
  327.                         if (rs!=null && rs.next())
  328.                                 machine_ID = rs.getInt(1);
  329.                         else {
  330.                                 ps.executeUpdate("insert into machine(hostname) values('"+hostName+"')");
  331.                                
  332.                                
  333.                                 rs = ps.executeQuery("select b.machine_id from machine b where b.hostname='"+hostName+"'");
  334.                                 if (rs!=null && rs.next())
  335.                                          machine_ID = rs.getInt(1);
  336.                         }
  337.                         conn.commit();
  338.                        
  339.                         ps.execute("lock table env_machine in exclusive mode");
  340.                         rs = ps.executeQuery("select env_id,machine_id from env_machine where env_id ="+ envID +" and machine_id ="+machine_ID);
  341.                        
  342.                         if (rs!=null && rs.next()==false)
  343.                                 {
  344.                                 ps.executeUpdate("insert into env_machine(env_id, machine_id, m_use) values("+envID+","+machine_ID+",'TestClient')");
  345.                                
  346.                                        
  347.                                
  348.                         }
  349.                         conn.commit();
  350.                        
  351.                        
  352.                         SimpleDateFormat df = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.US);
  353.                         SimpleDateFormat df_db = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  354.                        
  355.                 //get "test_no"
  356.                         //check existing test_no first
  357.                         // If same env_id , sc_id and start_time between +- 30 seconds, then it should be considered as same test
  358.                        
  359.                         ps.execute("lock table execution in exclusive mode");
  360.                         rs = ps.executeQuery("SELECT test_no FROM execution where env_id="+envID+" and sc_id="+scID+" and test_start between to_date('" + df_db.format(df.parse(testStart_timestamp)) + "','YYYY-MM-DD HH24:MI:SS') - 30 seconds and " +
  361.                                         "to_date('" + df_db.format(df.parse(testStart_timestamp)) + "','YYYY-MM-DD HH24:MI:SS') + 30 seconds ");
  362.                        
  363.                         if (rs!=null && rs.next())
  364.                                 {
  365.                                    test_no = rs.getInt(1);
  366.                                 }
  367.                         else {
  368.                 rs = ps.executeQuery("SELECT nextval for Test_Seq FROM SYSDUMMY1");
  369.                         if (rs!=null && rs.next())
  370.                                 test_no = rs.getInt(1);
  371.                         }
  372.                        
  373.                         //get exec_id
  374.                         rs = ps.executeQuery("SELECT nextval for Exec_Seq FROM SYSDUMMY1");
  375.                         if (rs!=null && rs.next())
  376.                         {
  377.                                 execId = rs.getInt(1);
  378.                         }
  379.             
  380.                         rs.close();
  381.                         rs = null;
  382.                        
  383.                        
  384.                         //execution
  385.                         String sqlcmd = "INSERT INTO Execution(Exec_ID,Test_No, Build_ID,sc_id, env_id,VU,TESTER,Levelload_Start,Levelload_End" +
  386.                                         ",Driver_Interval,Test_Start,Test_End, Description, machine_id) " +
  387.                                         "VALUES(" + execId + "," + test_no  + ","+ buildId + "," + scID + "," + envID + "," +
  388.                                         vu + ",'" + tester + "',to_date('" + df_db.format(df.parse(levelloadStart_timestamp)) + "','YYYY-MM-DD HH24:MI:SS')," +
  389.                                                         "to_date('" + df_db.format(df.parse(levelloadEnd_timestamp)) + "','YYYY-MM-DD HH24:MI:SS')," +
  390.                                                         Config.getInstance().histoInterval + "," +
  391.                                                         "to_date('" + df_db.format(df.parse(testStart_timestamp)) + "','YYYY-MM-DD HH24:MI:SS')," +
  392.                                                         "to_date('" + df_db.format(df.parse(testStop_timestamp)) + "','YYYY-MM-DD HH24:MI:SS'),'" +
  393.                                                         test_desc+ "',"+ machine_ID+")";
  394.                         //System.out.println(sqlcmd);
  395.                         ps.executeUpdate(sqlcmd);
  396.                        
  397.                        
  398.                         for (i = 0; i< testcases.size(); i++)
  399.                         {
  400.                           //case
  401.                           //Get case ID
  402.                                 caseId = 0;
  403.                                
  404.                                 rs = ps.executeQuery("SELECT nextval for Case_Seq FROM SYSDUMMY1");
  405.                                 if (rs!=null && rs.next())
  406.                                         caseId = rs.getInt(1);
  407.                                 //ps.close();
  408.                                 rs.close();
  409.                                 rs = null;
  410.                                
  411.                                 ArrayList casedetail = (ArrayList)testcases.get(i);
  412.                                 String tc_desc = casedetail.get(0).toString();
  413.                             sqlcmd = "INSERT INTO CASEINFO(CASE_ID, CASE_NAME, DESCRIPTION, EXEC_ID) VALUES(" +
  414.                             caseId + ", '" + tc_desc.substring(tc_desc.indexOf(" "), tc_desc.lastIndexOf(":")).trim() + "', '"+ tc_desc.substring(tc_desc.indexOf(":")+1, tc_desc.lastIndexOf(" ")).trim()+"'," + execId + ")" ;
  415.                             
  416.                             //System.out.println(sqlcmd);
  417.                                 ps.executeUpdate(sqlcmd);
  418.                                
  419.                                 //result
  420.                                 PreparedStatement ps1 = conn.prepareStatement("INSERT INTO TRESULT( CASE_ID, INTERVAL_SEQ" +
  421.                                 ", LOOP_COUNT, AVG, MIN, MAX, TOTAL_SLEEPTIME,ERROR_COUNT, EXE_COUNT,  Exec_ID) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
  422.                                
  423.                                 for (int j = 2; j<casedetail.size();j++) {
  424.                                         //System.out.println(casedetail.get(j).toString());
  425.                                         String tmp[] = casedetail.get(j).toString().split("\t");

  426.                                         ps1.setInt(1, caseId);
  427.                                         ps1.setInt(2, Integer.parseInt(tmp[0]));
  428.                                         ps1.setInt(3, Integer.parseInt(tmp[1]));
  429.                                         ps1.setInt(4, Integer.parseInt(tmp[2]));
  430.                                         ps1.setInt(5, Integer.parseInt(tmp[3]));
  431.                                         ps1.setInt(6, Integer.parseInt(tmp[4]));
  432.                                         ps1.setInt(7, Integer.parseInt(tmp[5]));
  433.                                         ps1.setInt(8, Integer.parseInt(tmp[6]));
  434.                                         ps1.setInt(9, Integer.parseInt(tmp[7]));
  435.                                         ps1.setInt(10, execId);
  436.                                         ps1.addBatch();
  437.                                 }
  438.                                
  439.                    
  440.                                 ps1.clearParameters();
  441.                     ps1.executeBatch();
  442.                    
  443.                         }
  444.                        
  445.                 conn.commit();
  446.                 
  447.                 
  448.                 conn.close();
复制代码
    简单来说,每一次的运行成功的API都在Qtool有记录,1个是成功次数增加1,再就是记录下了这次的相应时间。     可以结合如下的图来看看最新运作的效果:
      Snap 2013-08-05 at 11.05.37.png
     可以看到在每一个Interval(Interval长度可以调整的),Create和Delete的最大和最短以及平均的响应时间,单位为秒。
     在最终能得到一个总体的结果:
      Snap 2013-08-05 at 11.12.01.png
     可以看到在9个Interval,也就是Qtool统计了9次的记录,这里有5个VU仅仅只是跑Create和Delete的场景,最后得到的平均响应时间。
      Snap 2013-08-05 at 11.16.19.png
     这里的图片是用jfreechart,而Log是用log4j,各种资源基本都是Free的,而功能上基本不比商用工具差多少。
     最后说下我们找性能测试的压力点是首先找到系统的保和点。
     饱和点就是在Case不变的情况下,通过调整VU的数目或者ThinkTime的时间来动态获得最大的吞吐量(成功的次数)。一般情况下,得到的饱和点的曲线都是倒立的抛物线函数,顶点就是我们需要找到的饱和点。有时候顶点不是一个点,而是一个平台,就类似与减肥减不下来的某个平台,横轴很大的区间,但是纵轴几乎不变。
     需要说明的是,找饱和点是针对固定的Case,实际上,Case,VU数目以及ThinkTime,或者被测系统的硬件或者产品任何一个发生变化,饱和点就会漂移。饱和点的interval设置为15分钟以上,每次找饱和点大概需要半天的时间,不断去尝试才能找得到。
     找到后,花1-2小时做单用户的Saniety Test,8小时的Stress (系统资源100%),然后恢复库做72小时的Endurance(系统资源50%),所以你看到任何一个Perf的defect都是很昂贵的,需要投入很多的时间和完全占用Powerful的机器,也就是测试软件是免费的了。
     希望这个框架对有兴趣自己做性能的同仁有帮助。
     这个写起来确实很费时间,下次我还会写点别的内容,缓慢更新,希望测试版块能更有点人气。

使用道具 举报

回复
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-8-5 14:13 | 显示全部楼层
需要补充一点的就是,这样做性能测试还需要测量多次然后去取平均值,取平均值的方法不是简单地算术平均,需要算出方差来,比如7次测量结果都落在某个允许的方差内,才能认为这个测量结果是稳定的。
稳定的结果才能被接受,否则就得重现测量。
这也是计算机数学要求的知识。

使用道具 举报

回复
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-8-6 09:11 | 显示全部楼层
本帖最后由 crazypeter2005 于 2013-8-6 15:00 编辑

今天转载下自动化测试前辈 Carl J. Nagle的文章
1 http://safsdev.sourceforge.net/F ... ationFrameworks.htm

1.png 2.png
3.png

4.png

5.png

6.png

7.png

8.png

9.png

10.png

11.png

12.png

13.png

14.png

使用道具 举报

回复
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-8-6 09:11 | 显示全部楼层
本帖最后由 crazypeter2005 于 2013-8-6 15:04 编辑

Continue...
15.png
16.png
17.png
18.png
19.png
20.png
21.png
23.png
24.png



使用道具 举报

回复
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-8-6 09:12 | 显示全部楼层
本帖最后由 crazypeter2005 于 2013-8-6 15:07 编辑

Continue...
25.png
26.png
27.png
28.png
29.png
30.png

使用道具 举报

回复
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-8-6 09:13 | 显示全部楼层
本帖最后由 crazypeter2005 于 2013-8-6 15:11 编辑

Continue...
31.png
32.png
33.png
34.png
35.png
36.png
37.png
38.png
39.png
40.png


使用道具 举报

回复
认证徽章
论坛徽章:
64
罗罗诺亚·索隆
日期:2017-09-07 16:40:52itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03itpub13周年纪念徽章
日期:2014-10-08 15:19:03祖国65周年纪念徽章
日期:2014-09-28 15:11:01ITPUB14周年纪念章
日期:2015-10-26 17:23:44青年奥林匹克运动会-五人制曲棍球
日期:2014-09-12 15:36:032014年世界杯参赛球队:克罗地亚
日期:2014-06-26 18:34:17马上有钱
日期:2014-05-19 14:56:35马上有车
日期:2014-05-06 21:46:21
发表于 2013-8-6 09:16 | 显示全部楼层
本帖最后由 crazypeter2005 于 2013-8-6 15:13 编辑

个人觉得在实际项目中,关键字驱动过于复杂,难以维护,而简单的录制和回放更是不行的。唯一可行的就是按照我在第一次写的那种Framework,虽然耗时构建,但是维护起来才算是比较容易。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则

TOP技术积分榜 社区积分榜 徽章 电子杂志 团队 统计 虎吧 老博客 知识索引树 读书频道 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档 | IT博客
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛 | SAP ERP系统
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 网站律师 隐私政策 知识产权声明
京ICP备16024965号 北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表