发布新日志

  • 使用Loadrunner分别测量服务器响应时间及页面加载时间

    2013-08-06 10:10:16

      最近想对一网站进行性能测试,我们的初衷是把Server reponse时间和页面渲染时间区分看,这样我们就能判断是server这端的问题,还是网页前端HTML、CSS、JS等解析问题。
      因为性能测试还需要同时测试最大并发用户数及服务器资源使用情况,所以测试工具选择了Loadrunner,但此工具只是能统计服务器响应时间,无法测试页面渲染及加载时间。
      在网上搜索了一些,有一篇帖子是讲如何使用QTP写VBS脚本,然后使用QTP中Tools--Options--Run的"Alow other Mercury products to run tests and components"功能来执行性能测试。这种方法不好的地方在于我们得在机器上同时装QTP 和 Loadrunner, 那么有没有一种办法可以直接使用Loadrunner来写VBS脚本然后直接用Loadrunner跑VBS的脚本呢?
      查看了Loadrunner支持的协议,其中有一项是关于"VB Script. Vuser",使用该协议可以实现相应语言的脚本的编写,那不正好可以满足我的要求么?于是参考帖子“使用QTP统计页面加载时间”开始在Loadrunner中编写VBS脚本。
      以下在原帖子中的代码在Loadrunner中的应用:
    在Action模块中编写如下脚本:

    Public Function timeCount(url)

      Set dom = CreateObject("InternetExplorer.Application") 'Create an IE object

      Set lr = CreateObject("LoadRunner.LrApi") 'Create LoadRunner object

      dom.Navigate(url) 'Open the specific URL

      time_start = Now() 'To obtain statistics at the beginneing of time

      timer_start = timer() 'Get the current time in milliseconds

      dom.visible = True ' Set IE can be seen

      While dom.busy or (dom.readyState<>4) 'dom.readyState -- Got the IE status; when IE busy or is loading the readyState<>4, got the time duration

       wscript.sleep 1 'Time interval 1 ms, if the relatively long time interval, then is likely to take less than a state value

       Select Case dom.readyState 'To determine the value of dom.readyState

        Case 0  'IE is not initialized

          time0 = Now()

          timer0 = timer()

        Case 1 'Is sending Request

          time1 = Now()

          timer1 = timer()

        Case 2 'Request has been sent to complete

          time2 = Now()

          timer2 = timer()

        Case 3 'Can receive the part of response data

          time_3 = Now() 

          timer_3 = timer()

        Case 4 'Page is loaded

          time4 = Now()

          timer4 = timer()

        End select

       wend

       timeCount = "Statistics Start Time:"&start_time&vbcrlf&"time0:"&time0&vbcrlf&"time1:"&time1&vbcrlf&"time2:"&time2&vbcrlf&"time3():"&time3&vbcrlf&"time4:"&time4&vbcrlf&"To complete the initializtion of IE and send request:"&(timer1-timer_start)&"seconds"&vbcrlf&"To send the completion and acceptance of server-side part of the response data:"&(timer2-timer1)&"seconds"&vbcrlf&"100% to receive and complete the HTML content parsing:"&(timer4-timer2)&"seconds"&vbcrlf& "Total spent:"&(timer4-timer_start)&"seconds"

    End Function


    Public Function Action()

      SITEURL = "http://www.yourwebsite.com"

      result = timeCount(SITEURL) 'Returns running result

      MsgBox result 'Output to run As a result, the line can be commented out loadrunner ' For debugging, you can comment it out

    End Function 


    通过执行上述脚本发现如下几个问题:
    1. wscript.sleep 1 运行时提示缺少wscript对象,在网上查了一下,原因如下:
    wscript对象是windows宿主脚本对象,而且是windows系统默认提供的脚本操作对象,即无须重新创建就可以使用。所以你在vbs,js中可以直接使用wscript(或者wsh)的方法和属性,但这只是说你可以直接使用wscript,不代表wscript属于vbs。
    尽管QTP引入了vbs,但由于wscript不属于vbs,所以QTP未引入wscript,使用时自然报错。

    很多人提供了解决办法,有的说是没有创建对象的缘故,按要求加了Set WshShell = CreateObject("WScript.Shell") 也是不可以的,后来网上有人重新写了WScript供Loadrunner调用:
    在 Action 函数前放置 Sleep 过程 Sub Sleep(MSecs) ' loadrunner can't use wscript.sleep directly, the following code is trying to create Wscript. object Set fso = CreateObject("Scripting.FileSystemObject") If Fso.FileExists("sleeper.vbs")=False Then Set bjOutputFile = fso.CreateTextFile("sleeper.vbs", True) objOutputFile.Write "wscript.sleep WScript.Arguments(0)" objOutputFile.Close End If CreateObject("WScript.Shell").Run "sleeper.vbs " & MSecs,1 , True End Sub
    将Loadrunner中的 wscript.sleep 1 改为调用 Sleep(1)

    2.不支持其它的浏览器,因为页面渲染时间跟浏览器是息息相关的,用vbscript写的脚本目前只支持IE,因为创建只能创建IE对象。

    3.上述脚本中只能把时间以Message box的形式显示出来,如果使用Loadrunner测试的话,为了分析结果,我们需要把时间存成事务,更新后的脚本如下:

    Sub Sleep(MSecs) ' loadrunner can't use wscript.sleep directly, the following code is trying to create Wscript. object

        Set fso = CreateObject("Scripting.FileSystemObject")

        If Fso.FileExists("sleeper.vbs")=False Then

           Set bjOutputFile = fso.CreateTextFile("sleeper.vbs", True)

           objOutputFile.Write "wscript.sleep WScript.Arguments(0)"

           objOutputFile.Close

        End If

        CreateObject("WScript.Shell").Run "sleeper.vbs " & MSecs,1 , True

    End Sub

    Public Function timeCount(url)

      Set dom = CreateObject("InternetExplorer.Application") 'Create an IE object

      Set lr = CreateObject("LoadRunner.LrApi") 'Create LoadRunner object

      dom.Navigate(url) 'Open the specific URL

      time_start = Now() 'To obtain statistics at the beginneing of time

      timer_start = timer() 'Get the current time in milliseconds

      dom.visible = True ' Set IE can be seen

      While dom.busy or (dom.readyState<>4) 'dom.readyState -- Got the IE status; when IE busy or is loading the readyState<>4, got the time duration

       Sleep(1) 'Time interval 1 ms, if the relatively long time interval, then is likely to take less than a state value

       Select Case dom.readyState 'To determine the value of dom.readyState

        Case 0  'IE is not initialized

          time0 = Now()

          timer0 = timer()

        Case 1 'Is sending Request

          time1 = Now()

          timer1 = timer()

        Case 2 'Request has been sent to complete

          time2 = Now()

          timer2 = timer()

        Case 3 'Can receive the part of response data

          time_3 = Now() 

          timer_3 = timer()

        Case 4 'Page is loaded

          time4 = Now()

          timer4 = timer()

        End select

       wend

       server_response_transaction_timer = timer2 - timer1 ' Get the server

       page_render_transaction_timer = timer4-timer_2

       total_timer = timer4 - timer_start

       lr.log_message "The server response time is " + lr.eval_string(server_response_transaction_timer)

       lr.log_message "The page render time is " + lr.eval_string(page_render_transaction_timer)

       lr.log_message "Total page load time is " + lr.eval_string(total_timer)

       lr.set_transaction "server_response_time", server_response_transaction_timer, lr.PASS

       lr.set_transaction "page_render_time", page_render_transaction_timer, lr.PASS

       lr.set_transaction "total_page_load_time", total_timer, lr.PASS

     

    Public Function Action()

      SITEURL = "http://www.yourwebsite.com"

      result = timeCount(SITEURL) 'Returns running result

      'MsgBox result 'Output to run As a result, the line can be commented out loadrunner 

    End Function


    4.测试更新后脚本发现dom.readystate=2总是捕获不到,导致不能计算出事务时间,经过Debug发现,dom.readystate=2确实没有捕获到,但dom.readystate=3到是有很多次的时间捕获,后来想到当dom.readystate状态从2变化到3时,也可以认为服务器已经返回了响应,直接把readystate变成3的第一个时间值当做服务器返回响应的最后时间就可以,再次更新代码
    Sub Sleep(MSecs) ' loadrunner can't use wscript.sleep directly, the following code is trying to create Wscript. object

        Set fso = CreateObject("Scripting.FileSystemObject")

        If Fso.FileExists("sleeper.vbs")=False Then

           Set bjOutputFile = fso.CreateTextFile("sleeper.vbs", True)

           objOutputFile.Write "wscript.sleep WScript.Arguments(0)"

           objOutputFile.Close

        End If

        CreateObject("WScript.Shell").Run "sleeper.vbs " & MSecs,1 , True

    End Sub

     

    Public Function timeCount(url)

      Dim i

      i = 0

      Dim time_3(100)

      Dim timer_3(100)

      Set dom = CreateObject("InternetExplorer.Application") 'Create an IE object

      Set lr = CreateObject("LoadRunner.LrApi") 'Create LoadRunner object

      dom.Navigate(url) 'Open the specific URL

      time_start = Now() 'To obtain statistics at the beginneing of time

      timer_start = timer() 'Get the current time in milliseconds

      dom.visible = True ' Set IE can be seen

      While dom.busy or (dom.readyState<>4) 'dom.readyState -- Got the IE status; when IE busy or is loading the readyState<>4, got the time duration

       Sleep(1) 'Time interval 1 ms, if the relatively long time interval, then is likely to take less than a state value

       lr.log_message "The readyState is " + lr.eval_string(dom.readyState) ' For debugging, you can comment it out before running

       Select Case dom.readyState 'To determine the value of dom.readyState

        Case 0  'IE is not initialized

          time0 = Now()

          timer0 = timer()

        Case 1 'Is sending Request

          time1 = Now()

          timer1 = timer()

        Case 2 'Request has been sent to complete

          time2 = Now()

          timer2 = timer()

        Case 3 'Can receive the part of response data

          time_3(i) = Now() ' When dom.readystate is changed from 2 to 3, it means server has returned the response data, so the first value of Now() and Timer() stands for the server response time.

          timer_3(i) = timer()

          lr.log_message "when dom.readystate = 3 "& cstr(i) + " " + lr.eval_string(time_3(i)) ' For debugging, you can comment it out before running

          i = i+1 ' Because dom.reaystate = 3 may keep in a while

        Case 4 'Page is loaded

          time4 = Now()

          timer4 = timer()

          lr.log_message "4 start at " + lr.eval_string(time4) ' For debugging, you can comment it out before running

        End select

       server_response_transaction_timer = timer_3(0) - timer1 ' Get the server

       page_render_transaction_timer = timer4-timer_3(0)

       total_timer = timer4 - timer_start

       lr.log_message "The server response time is " + lr.eval_string(server_response_transaction_timer)

       lr.log_message "The page render time is " + lr.eval_string(page_render_transaction_timer)

       lr.log_message "Total page load time is " + lr.eval_string(total_timer)

       lr.set_transaction "server_response_time", server_response_transaction_timer, lr.PASS

       lr.set_transaction "page_render_time", page_render_transaction_timer, lr.PASS

       lr.set_transaction "total_page_load_time", total_timer, lr.PASS

    End Function


    Public Function Action()

      SITEURL = "http://www.yourwebsite.com"

      result = timeCount(SITEURL) 'Returns running result

      'MsgBox result 'Output to run As a result, the line can be commented out loadrunner 

    End Function


    5.还有一个问题就是当脚本执行完毕后,发现IE无法关闭,虽然表面上看似已经关闭了,其实进程还在运行,当批量进行执行脚本时,导致机子运行缓慢,解决方法是在vusr_end中将IE进程关闭
    Public Function Terminate() 

       Set ws=CreateObject("wscript.shell")

       ws.run "cmd /c taskkill /f /im iexplore.exe",0 

    End Function


    6.刚才测试的时候发现一个问题,如果在vusr_end中结束IE进程,在controller中跑的时候,有可能会导致正在运行中的其它并发用户正在使用的浏览器关闭,所以我们还不能使用上述的方法,暂时想到的办法是使用dom.quit 放置在timeCount函数最后。
      然后写一个批处理程序来运行controller,等所有的脚本运行完成后关闭ie进程,目前只是一个想法,后续实践后再分享。
    哈哈,至此使用Loadrunner测试页面加载时间的目的已经达到
  • [转]web_url函数的一个细节问题

    2013-07-03 15:22:30

    源文BLOG http://blog.csdn.net/wyingquan/article/details/210821
    web_url(); 步骤包含了思考时间,即使是在没有指定的情况下。
    解决方式:
            即使没有指定思考时间,系统也会自动为web_url("default.asp")步骤指定思考时间。
            在重播思考时间启用时该步骤会有10秒钟的暂停。忽略思考时间可以使它立即直接访问。
            在Analysis中如果选中筛选器中的包含思考时间选项就可以在结果中看到思考时间。
     以下是有关服务请求的细节知识:
            “在某些情况下运行脚本时,LoadRunner会加入它自己的思考时间。其中一种情况是当收到一个401错误时。当请求的cookie设置不正确时,有时应用服务器会返回401错误。LoadRuner一旦收到这个错误信息它就会等待10秒钟并且重新请求资源。这次LoadRunner将会向服务器发送正确的cookie,从而进行访问。”
            “401错误的存在要求站点运行正常。它的工作方式是这样的,第一次请求某个URL或着以一个新的会话返回了URL,服务器需要为此URL认证或指定一个session id,这样就会在错误的请求钟设置一个cookie信息。然后就会重新请求该URL,这次是使用的是一个有效的cookie,然后服务器发送你所请求的信息。” 
            “如果没有返回401错误的话就不能生成一个新的cookie。没有新的cookie的话就不能访问服务器。” 
            “在这里我们想要做的是让LoadRunner立即重新发送请求而不等待10秒。为了达到这个目的,请在脚本文件夹下default.cfg文件中的[Web]下面加入下面语句:”
             Retry401ThinkTime=0 
            “这样设置以后,从Vugen中再次运行脚本或把它加到新的场景中或在已有的场景中删除并重新加入后运行,就不会在重新请求资源时等待10秒钟了。”
  • TestLink Integration with Authorized Trac

    2012-03-12 09:26:37

    TestLink集成Trac, 网上有一篇文章是介绍了2种工具的集成,详细内容参见http://blog.csdn.net/coolcqz/article/details/4188236
    此种方法的集成是在可以匿名访问Trac的基础上实现的,也就是说如果Trac要求必须登录的话,这种方法是不适用的。
    Google上搜索了很久,找到一篇贴子讲的就是如何集成这种需要认证的Trac的,参见http://51elliot.blogspot.com/2009/01/trac-testlink-and-xml-rpc.html
    里面主要内容就是讲如何集成认证Trac的内容,主要的关键点在以下几方面:
    1. 保证Trac上已经安装了XML-RPC的插件
    2. 按照http://blog.csdn.net/coolcqz/article/details/4188236 中所定义的顺序进行配置
    3. 对需要验证的Trac,还需要做如下配置,红色字体部分为需要添加或更新的地方
    modified
    /../testlink/cfg/trac.cfg.php, added:
    define('BUG_TRACK_XMLRPC_HOST', 'http://username:password@YourIpAddress');

    modified /../testlink/lib/bugtracking/int_trac.php:
    $this->xmlrpcClient = new IXR_Client(BUG_TRACK_XMLRPC_HOST.$tracProjectName.'/login/xmlrpc');

    modified
    /../testlink/third_party/xml-rpc/class-IXR.php

    function query() {
    $args = func_get_args();
    $method = array_shift($args);
    $request = new IXR_Request($method, $args);
    $length = $request->getLength();
    $xml = $request->getXml();
    $r = "\r\n";
    $credentials = "";
    if ($this->user != "") {
    $credentials = base64_encode($this->user . ":" . $this->pass);
    }

    $request = "POST {$this->path} HTTP/1.0$r";
    $request .= "Host: {$this->server}$r";
    if ($credentials != "") {$request .= "Authorization: Basic {$credentials}$r";}

    function IXR_Client(
    $server, $path = false, $port = 80, $timeout = false) {
    if (!$path) {
    // Assume we have been given a URL instead$bits = parse_url($server);
    $this->server = $bits['host'];
    $this->port = isset($bits['port']) ? $bits['port'] : 80;
    $this->user = isset($bits['user']) ? $bits['user'] : '';
    $this->pass = isset($bits['pass']) ? $bits['pass'] : '';
  • TestLink - Search Test Cases by Author

    2012-03-07 11:57:20

    Testlink中一个模块是专门用来查询Test Cases,此查询模块中包括了几乎Test Case中的所有查询条件,唯独没有按Test Case的创建者查询的条件.
    仔细研究了其数据库结构及lib/testcase/tcSearch.php文件后,按如下步骤可以完成按Author查询TestCase的功能:
    1. 查看数据库所有表
    • 数据库表中,与test case 的 author 有关的数据存放于tcversions这个表中,此表中存放的是author的id,即author_id
    • 要想知道Author的名称的话,必须交叉访问users表中的login字段才可以得到,这2个表是通过 users.id = tcversions.author_id 关联起来的
    2. 了解了表结构,再来看看lib/testcase/tcSearch.php文件中的内容
    • $tables = tlObjectWithDB::getDBTables(array()) 中加入 'users'这个表
    • 在$sqlPart2=""中加入联合查询users表,JOIN {$tables['users']} US ON US.id = TCV.author_id
    • 在 $sqlFields = ""; 中加入author名称的字段”US.login“ 
    • 在if(args->name != ""){...} 下面和if(args->summary != "")上面加入如下代码以便按用户输入的Author name进行模糊查询

    if($args->login != "")
    {        
      $args->login = $db->prepare_string($args->login);                  
      $filter['by_author'] = " AND US.login like '%{$args->login}%' ";    
    }  

    • 在$iParams = array()中加入如下参数,主要作用是进行参数的名称及类型的定义
    "login" => array(tlInputParameter::STRING_N,0,50),

    3. 上表内容修改完成后,还要到gui\templates\testcases中找到“tcSearchForm.tpl“ 进行页面上显示内容的修改
    • 在 {lang_get var="labels" s='' 中 author 值, 这个是在页面上查询页面上显示出来的名称
    • 找好要显示author字段的位置,并加入如下代码

    <tr>
    <td>{$labels.author}</td>
    <td><input type="text" name="login" size="{#TCNAME_SIZE#}" maxlength="{#TCNAME_MAXLEN#}" /></td>
    </tr> 


    • 注意 name 的值必须与lib/testcase/tcSearch.php - $iParams = array()中定义的值一致,否则查询不会成功; 如 在tcSearch.php中定义的名称为"login",在此处的name也必须是"login"
    4. 如果有需要可以重启apache服务
    5. 重启成功后,到 Search Test Case页面,可以看到Author字段,也可按Author进行搜索,如下图

  • Test Link integration with Fogbugz On Demand

    2012-03-02 16:39:36

    最近在研究Testlink集成Fogbugz。
    Fogbugz提供了2种工作模式,一种是下载Fogbugz到本地进行安装配置,另一种方法是托管服务(Fogbugz On Demand),即向Fogbugz申请项目网址,不需要关心安装配置,只需要关心如何使用。
    本公司使用的正是第2种模式,很不幸的是Testlink只提供了第1种方式的集成方法。
    经过3天的研究,走了不少弯路,终于成功实现了Testlink与Fogbugz On Demand的集成,以下把研究的过程Share出来:
    1. 搞明白Testlink集成BTS(Bug Tracking System)的原理
    (1)Testlink是通过config.inc.php来确定与哪个BTS进行集成的,在些我们在[Bug Tracking System]部分中找到$g_interface_bugs = 'FOGBUGZ'
    (2)Testlink通过lib/bugtracking/文件夹中相应类库(如 int_fogbugz.php)向Bug库发送请求并处理返回结果,发送请求时的参数需要在cfg文件夹中相应的配置文件(如fogbugz.cfg.php)进行设置。
    • 首先要搞清楚的问题是,  Fogbuz是如何处理请求并返回结果的?在网上搜索了一下,Fogbugz提供了Api接口,它可以处理按照一定格式发送的请求并返回Xml. API格式定义请参见:http://fogbugz.stackexchange.com/fogbugz-xml-api
    • 比如,你要想查询Fogbugz中的某个Bug并要求返回的结果中包括某个值,则要求按如下地址发送请求:htt://[Your Fogbugz Website]/?token=xxxxxxx&cmd=search&q=25&cols=sStatus
    • 其中token是登录后返回的值; cmd相当于执行的命令,可以是search也可是edit,new等等;q指要查询的关键字,可以是id号也可是其它;cols指search命令后可以返回的值,这个值可以有sStatus,sTtile等
    • 在网上已经有人将Fogbugz的API进行了封装,直接调用封装好的方法可以更方便的发送请求,参见网址https://github.com/LearningStation/fogbugz-php-api
    • 将其中的api.php下载下来,也可以从我上传的附件中得到,放置到testlink/third_library目录下的某个文件夹中,在这,我新建了一个叫‘fogbugz-api’ 
    2. 修改int_fogbugz.php及fogbugz.cfg.php文件
     确定了这些发送请求的机制、格式及必须字段等,就可以对int.fogbugz.php及fogbugz.cfg.php文件进行修改了,修改后的代码参见附件.
    3. 重启apache服务
    4. 注意事项: 
       fogbugzapi.php需要调用php_curl.dll, 先做如下配置,否则会出现 Fatal error: Call to undefined function curl_init() 
    1. 找到php.ini
        修改extension=php_curl.dll 把前面的分号去掉
    2. Windows下处理方式: 在Php安装目录下找到如下模块并将其复制到windows/system32下,并重启Apache
        php5ts.dll
        libeay32.dll
        ssleay32.dll
    3. Linux下需要安装php_curl扩展,生成curl.so文件



  • 转 - Trac系列(6):用户管理和身份验证

    2011-11-11 15:09:10

    关于TRAC的权限设置,可参考如下文章,写的简单易懂。
    转载地址:http://hi.baidu.com/xuexiba/blog/item/23980ffafc7c229459ee90dd.html#0

    Trac系列(6):用户管理和身份验证
    2007-09-05 22:44
    在Trac中,添加、删除、配置用户账户以及身份验证的过程依赖于运行Trac的方式,也就是使用哪种方式、在哪种Web服务器上运行Trac。

    下面介绍使用Tracd时如何管理用户帐号以及进行身份验证。

    在以前的讲解中,我们建立了一个Angel项目的Trac环境,用以下的命令:
    C:\Python24\Scripts>..\python tracd -p 8001 C:\SCM\Angel\Angel_Trac
    来运行Trac环境。

    虽然使用浏览器访问该项目时,页面上方也有一个“Login”连接,但是是无法使用的,点击了会报错,提示:
    “Authentication information not available. Please refer to the installation documentation.”
    这是因为我们没有为该项目设置用户、分配权限和密码。要使Login可用,我们需要做以下三个工作:

    1. 创建用户并分配权限
    过程如下图:

    现在我们的Angel项目有了一个admin的用户,是Trac的管理员。
     

    2. 给用户设定密码
     

    Tracd支持Basic和Digest两种身份验证方式,默认使用Digest,在这里只介绍Digest方式,这是来源于Apache的用户管理和身份验证方式。

    Digest方式需要提供一个特定格式的密码文件,可以使用Apache的htdigest命令来生成密码文件。如果没有Apache,可以使用下面的python脚本来生成密码(我就是使用的这种方式)

    from optparse import OptionParser
    import md5

    # build the options
    usage = "usage: %prog [options]"
    parser = OptionParser(usage=usage)
    parser.add_option("-u", "--username",action="store", dest="username", type = "string",
    help="the username for whom to generate a password")
    parser.add_option("-p", "--password",action="store", dest="password", type = "string",
    help="the password to use")
    (options, args) = parser.parse_args()

    # check options
    if (options.username is None) or (options.password is None):
    parser.error("You must supply both the username and password")

    # Generate the string to enter into the htdigest file
    realm = 'trac'
    kd = lambda x: md5.md5(':'.join(x)).hexdigest()
    print ':'.join((options.username, realm, kd([options.username, realm, options.password])))
    将上面的代码保存到一个新文本文件中,并重命名该文件的扩展名为py(假定修改后该文件是
    trac-digest.py,放在了C:\SCM目录),使用下面的命令
    python trac-digest.py -u username -p password >> c:\digest.txt
    具体到我们的项目就是
    C:\Python24>python c:\scm\trac-digest.py -u admin -p admin >>c:\scm\digest.txt
    会在c:\scm\目录下生成一个digest.txt的密码文件,该文件的格式是“用户名:范围名(readlm名称,默认是trac):密码的密文”。
    如下图

    可以多次执行上面的命令在一个密码文件中为多个用户设定密码。

    3. 使用带验证选项的行命令启动tracd
    命令如下
    tracd -p 8080 --auth=project1,/path/to/users.htdigest,mycompany.com /path/to/project1
    或者
    tracd -p 8080 --auth=*,/path/to/users.htdigest,mycompany.com /path/to/project1
    具体到我们的项目就是
    C:\Python24\Scripts>..\python tracd -p 8001 --auth=Angel_Trac,c:\scm\digest.txt,trac C:\SCM\Angel\Angel_Trac
    或者
    C:\Python24\Scripts>..\python tracd -p 8001 --auth=*,c:\scm\digest.txt,trac C:\SCM\Angel\Angel_Trac
    需要注意:
    a.红色的部分,是realm(范围),在行命令中的realm必须与生成密码文件的脚本总的realm相同。
    b.蓝色的部分,也就是--auth的第一个参数,不是项目名称,是Trac环境全路径的最后一个目录。
    ok,现在打开浏览器,访问Trac网站,点击“Login”,就会弹出登陆对话框进行身份验证了。如下图

    是不是感觉很麻烦呢,没关系,Trac有插件来扩展这些功能,我们随后就会介绍如何用插件来管理用户和配置Trac。

    最后,需要强调的是,这篇文章只是对使用tracd运行Trac的用户管理,其他方式请参考Trac官方网站的指南。


    参考文献:
           Trac的官方指南中的TracAdmin介绍。
        Trac的官方指南中的Trac Permissions介绍。
    Trac的官方指南中的Tracd介绍 。
  • Trac - 如何自定义RoadMap中的内容

    2011-11-11 11:22:15

    曾经写过一篇文章介绍如何自定义字段及流程的文章(http://www.51testing.com/index.php?uid-49689-action-spacelist-type-blog-itemtypeid-11268),今天介绍如何自定义RoadMap.
    原文参见:http://trac.edgewall.org/wiki/TracRoadmapCustomGroups
    Trac中的roadmap是对milestone的一个集合统计,如果你在创建ticket时选择了相应的milestone,则打开roadmap tab时,默认显示如下:

    这个图中只是显示了已解决及未解决的tickets数,根据我们自定义流程中文章中的介绍,trac允许用户自已添加一些流程,如果用户也想在roadmap中体现出来自定义的status,该如何做呢?

    1. 比如用户在trac.ini [ticket-custom]中自定义了testing流程,定义内容如下:

    testing = new, accepted, needs_work, assigned, reopened -> testing
    testing.name = Submit to report for testing
    testing.operations = set_owner
    testing.permissions = TICKET_MODIFY

    2. 如果用户想在roadmap中看新增加的状态,可做如下处理:

    (1)在trac.ini文件中增加[milestone-group] section,其中内容包括如下:

    [milestone-groups]
    # The definition consists in a comma-separated list of accepted status.
    # Also, '*' means any status and could be used to associate all remaining
    # states to one catch-all group.
    # Qualifiers for the above group (the group must have been defined first):
    
    # Definition of a 'closed' group:
    closed = closed # 此closed状态,必须已经提前定义过
    closed.order = 1
    closed.query_args = group=resolution
    closed.overall_completion = true 
    
    # Definition of an 'in testing' group:                                 
    inQA = testing # 此testing状态,必须已经提前定义过
    inQA.order = 2
    inQA.css_class = new
    inQA.label = in testing
    
    # Definition of an 'active' group:
    active = *
    active.order = 3
    active.css_class = open
    active.label = in progress
    
    # The CSS class can be one of: new (yellow), open (no color) or
    # closed (green). New styles can easily be added using the following
    # selector:  `table.progress td.<class>`
    

    完成后,重新trac server,可得到如下roadmap:

    Trac roadmap with multiple groups

    3. 上述内容的解释

    <groupname>可以是一个或多个status,多个status之间用逗号分隔;使用*则意味着匹配所有在[milestone-group]中尚未定义的状态
    <groupname>.order表示在上图progress bar中显示的位置
    <groupname>.query_argsOptional extra parameter for the query
    <groupname>.overall_completiontrue/false - count in the overall completion statistic
    <groupname>.css_class定义progress bar中显示的颜色new (yellow), open (no color) or closed (green). 
    <groupname>.label显示在progress bar中的名称

  • Sikuli+RobotFramework集成

    2011-10-27 12:09:05

    我经手的一个手机项目曾经用Sikuli做自动化,但是其难看及简陋的test report着实让我郁闷的了一段时间,找来找去,只想到了一个替换方案,详细内容参见如何保存Sikuli的运行结果,但是这个方案仍然没有办法提供一个完整的结果统计数据,后来看到了Robot Framework这个测试框架,它本身并不能对被测试系统做任务测试,是通过第三方的工作及TestLibraries驱动完成的,但是它提供了一套完整的test report及log管理,后来想想是否可以将Sikuli与Robot Framework集成在一起呢?

    抱着这种想法,在网上搜索(当然是google上而且是英语的关键字搜索)了一下,有一个贴子http://groups.google.com/group/robotframework-users/browse_thread/thread/d92ce2ec8b530d42?fwc=1&pli=1有人提到了在Mac版下如何进行2者的集成,后来又有贡献者提供了在window xp下2者集成的详细过程http://blog.mykhailo.com/2011/02/how-to-sikuli-and-robot-framework.html

    以下我只介绍在Mac OS下如何进行2者的集成,需要在windows OS的童鞋可参考http://blog.mykhailo.com/2011/02/how-to-sikuli-and-robot-framework.html

    1. Robot Framework 集成 Sikuli的工作原理如下:

       Robot Framework是基于Python的,它提供了2种方法执行脚本, 一个是python (pybot), 另一个是jython(jybot), Robot Framework支持自定义的libraries及第三方工具; Sikuli是通过jython执行脚本;初步的想法是:把Sikuli写成的脚本作为libraries供Robot调用,执行脚本时使用'jybot'驱动Sikuli工具完成, 如下集成结构图


    2. 操作步骤:
    • (1)安装
    • (2)确保Sikuli脚本可以正确被java执行
    • (3)将Sikuli脚本转换成Robot Framework可识别的libraries
    • (4)执行脚本
    • (5)运行结果
    以下按上述步骤详述:

    (1)安装(以下是针对Mac OS的)
           具体的安装过程请参见各工具的官方网站,其中Robot Framework的安装可参见Installation 

    (2)确保Sikuli脚本可以正确被java执行

        为了保证Sikuli的脚本可以通过Robot Framework驱动执行,我们首先要确保sikuli的脚本可以通过命令行的形式执行(java命令),详细过程参见 https://answers.launchpad.net/sikuli/+faq/1110
    • a.创建一个测试文件夹,本案例为 /work/RobotSikuli
    • b.找到下载完成的Sikuli.App文件,右击“Show Package Contents”, 将其目录“/Contents/Frameworks”文件夹拷贝至/work/RobotSikuli
    • c.打开/work/RobotSikuli/Frameworks文件夹,新建一个change-path.sh脚本,脚本中包括如下内容:
    #!/bin/sh
    for lib in *.*lib
    do   
      for ref in `otool -L $lib | grep executable_path | awk '{print $1'}`   
      do     
        install_name_tool -change $ref @loader_path/../Frameworks/`basename $ref` $lib   
      done
    done
    • d.打开终端,跳转至/work/RobotSikuli/Frameworks目录,执行如下命令
    1. chmod +x  change-path.sh:先用命令把批处理文件修改为可执行文件
    2. ./change-path.sh :执行该文件。
    3. 然后输入”otool -L *.*lib“: 确保脚本中所有libraries中@executable_path都被@loader_path "即/../Frameworks"所替换
    • e. 在/work/RobotSikuli/中新建一个名称为tmplib的文件夹,然后做如下运作
    1. 找到下载完成的Sikuli.App文件,右击“Show Package Contents”, 解压目录“/Contents/Resources/Java/sikuli-script.jar”
    2. 在解压后的文件夹中,找到“sikuli-script/META-INF/lib/libScreenMatchProxy.jnilib”并拷贝至/work/RobotSikuli/tmplib中
    3. 在终端中执行“otool -L /work/RobotSikuli/tmplib/libScreenMatchProxy.jnilib”:确保原先所有指向 /opt/local/lib/*.dylib转为指向 @loader_path/../Frameworks/*.dylib
        
        如果执行步骤d,e时有问题,可以直接从网上下载整理好的文件执行,地址为:http://people.csail.mit.edu/vgod/sikuli/sikuli-libs.zip
    • f.在步骤e-2中解压好sikuli-script.jar文件夹中,找到“/sikuli-script/Lib”文件夹,将整个Lib文件夹拷贝至/work/RobotSikuli目录下;然后再将未解压的sikuli-script.jar包也拷贝到/work/RobotSikuli目录中
    • g.下载patch http://people.csail.mit.edu/vgod/sikuli/sikuli-mod.patch ,并该下载好的文件放在/work/RobotSikuli/Lib/sikuli目录中,然后在终端中执行如下动作
    1. cd到/work/RobotSikuli/Lib/sikuli目录下
    2. patch < sikuli-mod.patch
    这个patch是给相同目录下的Screen.py及Sikuli.py打patch

    Note: 但我不知道为什么要这么做,我根据上述步骤执行时提示“hunks failed”,但是后来验证该步并没有影响后续的动作
    • h. /work/RobotSikuli/中新建一个名称为test_suites的文件夹,在这个文件中放置写好的sikuli脚本,附件中有一个已经写好的sikuli脚本可以下载下来备用( Calc1.sikuli.zip(58.9 KB)

    • i. /work/RobotSikuli/中新建一个名称为my_lib的文件夹,把附件中文件“Calc.py”拷贝该文件夹中,现在我们来看一下目前RobotSikuli文件夹结构

    • j. 打开终端,先跳转至/work/RobotSikuli/目录下,然后执行如下语句:java -d32 -Dpython.path=Lib -jar sikuli-script.jar Calc1.sikuli, 执行完成后,可以查看结果,这样就验证了Sikuli确实可以在命令行的情况执行脚本
    (3)将Sikuli脚本转换成Robot Framework可识别的libraries
    • a. 根据Robot中说明,引用自定义的libraries时最简单的办法就是直接将一个module(in Python)或class(in Jython)映射成Robot中的"keywords name(相当于类)", 参考放在mylib中的sikuli脚本的.py文件,其就相当于是一个module了,可以直接当作test libraries被调用。
    • b. 接下来我们要做的就是通过Robot驱动Sikuli的脚本运行,我们需要做的就是在使用Robot的格式想一个全新的脚本,脚本中所用的keywords可以直接引用.py文件中的方法名;Robot脚本参见附件 robot_calc1.txt(259 B )

    (4)执行脚本:根据1中所介绍的原理,我们通过jybot这个工具执行Robot脚本,执行过程如下:

    1.打开终端,切换至/work/RobotSikuli目录下
    2.export CLASSPATH=sikuli-script.jar
    3.jybot --pythonpath=myLib:Lib  test_suites/robot_calc1.txt



    (5)运行结果第2个case应该执行失败的,且查看log详细日志时,它的提示是“INFO FAIL:Actual result '4' is not equal to expected result '5'”,但在Report的状态栏显示的是PASS;后来想一下,这个提示只是在INFO中,RobotFramework并不知道没有办法知道INFO是通过还是失败。
              请公司的架构帮忙看了一下RobotFramework的代码,原来RobotFramework生成test report的机制是通过捕获异常来判断是否成功还是失败,在他的建议下,把原来写的sikuli(/myLib/Calc1.py)的脚本做了修正, 修改内容如下:将原有脚本中的print改为raise exception

    原来的脚本:

        def verifyResult(self, *args):
            expected_result = str(eval(''.join(args)))
            actual_result = self.getResultFromClipboard()
            #verification
            if actual_result == expected_result:
                print("PASS: Action performed correctly and result equals %s" % expected_result) 
            else:
                print AssertionError("FAIL: Actual result '%s' is not equal to expected result '%s'" % (actual_result, expected_result))

    现在的脚本:

        def verifyResult(self, *args):
            expected_result = str(eval(''.join(args)))
            actual_result = self.getResultFromClipboard()
            #verification
            if actual_result == expected_result:
                raise AssertionError("PASS: Action performed correctly and result equals %s" % expected_result) 
            else:
                raise AssertionError AssertionError("FAIL: Actual result '%s' is not equal to expected result '%s'" % (actual_result, expected_result))

         至此, Sikuli与RobotFramework的集成全部搞定,附上生成的报告



    序:感叹啊,高手就是高手,人家只用了几个小时就把RobotFramework的代码及机制全搞清楚了,很快就能定位问题所在,这就是差距啊,我什么时候才能达到这种水平呢?
  • Automation One By One - Robot Framework - User Keywords

    2011-08-26 10:15:17

    Automation One By One - Robot Framework - Test Case 这一章中已经简单介绍了keywords相关概念,我们知道keywords分为2大类,一类是Librarykeywords,另一类是userkeywords。对于Librarykeywords我们不在这边做详细的介绍了,本章主要介绍如何创建userkeywords.


    1. userkeywords的语法(syntax)- 与test case的语法类似,唯一不同在于存放的位置不同,test case放在test case table中,keyword放在keywords table中

    ***keywords*** 
      userkeywords_name  [Arguments]

    Examples

    Example without arguments
    ***keywords*** 
      Open Login Page
        Open Browser  http://www.baidu.com
        Title Should Be  百度知道

    Example with arguments
    ***keywords*** 
      Title Should Start With  [Arguments]  ${expected_value}
        ${title} =  Get Title
        Should Start With  ${title}  ${expected_value}
        

    2. keywords table中可以加入settings命令
       为了与keywords的name区分,keywords table中的命令使用[settings]格式。
       可以使用的settings命令如下:

    [Documentation]
    Used for setting a user keyword documentation.
    [Arguments]
    Specifies user keyword arguments.
    [Return]
    Specifies user keyword return values.
    [Timeout]
    Sets the possible user keyword timeout. Timeouts are discussed in a section of their own.


    3. userkeywords中的arguments格式

    4. userkeywords name中嵌入arguments.
    • 语法:在userkeyword名中嵌入argument后不能再使用[arguments]添加其它的参数了。
    • 在userkeyword名中的arguments依赖于调用userkeyword时传递的值,如下例子
    百度高级搜索页面
    *** Settings ***
    Library         Selenium Library   15
    Test Setup     Start Selenium Server
    Test Teardown  Stop Selenium Server  

    *** Testcases ***
    TC1  [Documentation]  embedded arguments
      Open Browser    http://news.baidu.com/advanced_news.html
      Select 最近一年 from list
      Select 最近一周 from list 
      Select 最近一小时 from list
      comment 上述的值应该在下拉列表范围内,如果不在,系统会报错

    *** Keywords ***
    Select ${date} from list     
      Sleep  3s
      Select From List  name=mt   ${date}

    • 需要注意的事,如果传递的值是带有空格的,如 最近 一年, 在调用函数时必须用引号引起来,说明要传递的是一个参数,否则,系统会认为是2个值。
         Select "最近 一年" from list

    5. 定义一个可以返回值的userkeywords - 实现这个功能,可以借助settings-[Return]命令实现

    *** Keywords ***
    Return Value keyword
      [Return]  pass


    Return One Value     [Arguments]     ${arg}            
        Do Something     ${arg}            
        ${value} =     Get Some Value            
        [Return]     ${value}


    6. 调用userkeywords

      从开发的角度讲,userkeywords及Librarykeywords相当于一个个封装好的函数,在test case中使用这些keywords时与调用函数的方法一样。

    调用keywords
    ***Test Cases***
      Open Login Page
      Title Should Start  百度












  • Automation One By One - Robot Framework - 变量及赋值

    2011-07-29 11:12:02

    前面已经提到变量有三类,一类是scalar variable ${variable},一类是list variable,还有一类是内置(Built-in)的变量.

    1. 给scalar variable类型的变量赋值:${scalar variable}

    (1)${scalar variable}=  simple strings
    (2)${scalar variable}=  objects
        无论scalar类型的变量被赋予什么值,这个值先被转换成Unicode,然后再按字符串显示出来。转换的过程相当于调用这个对象的__unicode__,__str__

    2. 给list variable赋值: @{variable}

    (1)列表变量中的值,可以是一个,也可以是多数(无数),还可以是空
    (2)列表变量的值通常是字符类型的,其它类型的也可以
    (3)可以通过@{NAME}[i]形式来访问列表中的某一个值
    (4)可将list variables当作scalar类型的变量使用,使用方法是将list变量的符号从@替换成$就可以了;这种表达式让我们使用built-in类库及Collection类库中list相关的keywords成为可能。
    (5)list变量可以应用在setting table中,但仅限于一部分的settings命令可用。
    • 对于Library及Resource的settings来说list变量不能作为其name使用,只能作为argument使用
    • 对于Setup/Teardown的settings来说list变量不能当作keyword的name使用,只能作为argument使用
    • 对于tag的settings来说可以随意使用
    • 对于不支持list变量的settings来说,我们可以使用scalar变量的形式来表达(如library及setup中的name不能使用list变量,我们可以用scalar变量代替list变量)
    Using list variables with settings
    Settings Value Value Comment
    Library ExampleLibrary @{LIB ARGS} # This works
    Library ${LIBRARY} @{LIB ARGS} # This works
    Library @{NAME AND ARGS}   # This does not work
    Suite Setup Some Keyword @{KW ARGS} # This works
    Suite Setup ${KEYWORD} @{KW ARGS} # This works
    Suite Setup @{KEYWORD}   # This does not work
    Default Tags @{TAGS}   # This works


    3. 给Envrionment Variables赋值:%{ENV_VAR_NAME}
    (1)只能赋值string类型的值
    (2)可于
    OperatingSystem library 类库中的keyword一块应用


    4. 可以将keyword中返回的值作为变量存储

    ${keyword_return_value}=  Get Matching Xpath Count  [locator]
    ${keyword_return_value}=  Get Element Attribute  [locator]
    ...
    5. 内置Build-in变量

    Available operating-system-related built-in variables
    Variable Explanation
    ${CURDIR} An absolute path to the directory where the test data file is located. This variable is case-sensitive.
    ${TEMPDIR} An absolute path to the system temporary directory. In UNIX-like systems this is typically /tmp, and in Windows c:\Documents and Settings\<user>\Local Settings\Temp.
    ${EXECDIR} An absolute path to the directory where test execution was started from. New in Robot Framework 2.1.
    ${/} The system directory path separator. / in UNIX-like systems, \ in Windows.
    ${:} The system path element separator. : in UNIX-like systems and ; in Windows.

    Variable Explanation Available
    ${TEST NAME} The name of the current test case. Test case
    @{TEST TAGS} Contains the tags of the current test case in alphabetical order. Test case
    ${TEST STATUS} The status of the current test case, either PASS or FAIL. Test teardown
    ${TEST MESSAGE} The possible error message of the current test case. Test teardown
    ${PREV TEST NAME} The name of the previous test case, or an empty string if no tests have been executed yet. Everywhere
    ${PREV TEST STATUS} The status of the previous test case: either PASS, FAIL or an empty string when no tests have been executed. Everywhere
    ${PREV TEST MESSAGE} The possible error message of the previous test case. Everywhere
    ${SUITE NAME} The full name of the current test suite. Everywhere
    ${SUITE SOURCE} An absolute path to the suite file or directory. New in Robot Framework 2.5. Everywhere
    ${SUITE STATUS} The status of the current test case, either PASS or FAIL. Suite teardown
    ${SUITE MESSAGE} The full message of the current test suite, including statistics. Suite teardown
    ${OUTPUT FILE} An absolute path to the current output file. Has different values during execution when outputs are split. Everywhere
    ${LOG FILE} An absolute path to the current log file or string NONE when no log file is created. Has different values during execution when outputs are split. Everywhere
    ${REPORT FILE} An absolute path to the report file or string NONE when no report is created. Everywhere
    ${SUMMARY FILE} An absolute path to the summary file or string NONE when no summary is created. Everywhere
    ${DEBUG FILE} An absolute path to the debug file or string NONE when no debug file is created. Everywhere
    ${OUTPUT DIR} An absolute path to the output directory. Everywhere












  • Automation One By One - Robot Framework - 数据类型

    2011-07-29 10:19:31

    Robot Framework中的数据类型均是通过变量(Variable)来表达的。

    Robot Framework提供以下几种数据类型:

    1. 字符串:${variable}, ${get value from keyword}, 80 ...  默认均是字符串类型
    2. 数值型:表达数值型,必须使用${Numbers}来表示
    Test Case Action Argument Argument Comment
    Example 1A Connect example.com 80 # Connect gets two strings as arguments
    Example 1B Connect example.com ${80} # Connect gets a string and an integer
    Example 2 Do X ${3.14} ${-1e-4} # Do X gets floating point numbers 3.14 and -0.0001

    3. 布尔值

    Test Case Action Argument Argument Comment
    Boolean Set Status ${true}
    # Set Status gets Boolean true as an argument

    Create Y something ${false} # Create Y gets a string and Boolean false


    4. Null/None值

    Test Case Action Argument Argument Comment
    None Do XYZ ${None}
    # Do XYZ gets Python None as an argument
    Null ${ret} = Get Value arg # Checking that Get Value returns Java null

    Should Be Equal ${ret} ${null}

    上述这些类型的变量均是大小写不敏感的,即 ${True} and ${true} 是相等的. 另外, ${None} and ${null} 属于属于同义词。

    5. 空格、Empty

    Robot Framework允许你使用${SPACE},${EMPTY}来表示空格及空

    Test Case Action Argument Argument
    One Space Should Be Equal ${SPACE} \ \
    Four Spaces Should Be Equal ${SPACE * 4} \ \ \ \ \
    Ten Spaces Should Be Equal ${SPACE * 10} \ \ \ \ \ \ \ \ \ \ \
    Quoted Space Should Be Equal "${SPACE}" " "
    Quoted Spaces Should Be Equal "${SPACE * 2}" " \ "
    Empty Should Be Equal ${EMPTY} \

  • Automation One By One - Robot Framework - 优化Test Case

    2011-07-12 09:43:58

    还是以前面提到成功登录百度账号的feature为例。

    *** Settings ***
    Library       Selenium Library
    Resource      /Path/to/Variable.txt
    Resource      /Path/to/loginkeywords.txt
    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Open Login Page
      Enter Username  @{username}[0]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Successful  @{username}[0]

    仔细想想这个test case是不是还有什么不完美的地方呢?

    我们说一个test case最大的特性就是repeatability, 即保证每次执行时步骤都是可重现的,output的也都是一样的。

    看看上面这个test case,它是否具有可重现性的特质呢? 如果用户已经登录了,这面的脚本是否可以执行通过?

    如何解决这个问题?
    • 在test case的真正代码执行前,我们先判断一下是否已经登录,如果已经登录则执行退出运作然后再执行登录的操作,如果没有登录则执行登录操作。
    • 我们修改一下test case
      • 先查找一下有没有退出按钮
      • 如果有单击退出
      • 如果没有再执行接下来的脚本

    *** Settings ***
    Library       Selenium Library
    Resource      /Path/to/Variable.txt
    Resource      /Path/to/loginkeywords.txt
    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      ${status}=  Call Selenium Api  is_element_present  link=登出 (将是否存在登出按钮的结果当成变量存储起来)
      Run Keyword If  ${status} == True  Click  link=登出 (以变量为条件进行判断)
      Open Login Page
      Enter Username  @{username}[0]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Successful  @{username}[0]

      • 为了方便用户理解,我们把刚才新增的脚本放在loginkeywords.txt中.
    • 修改过后的Test Case及LoginKeywords如

    *** Settings ***
    Library       Selenium Library
    Resource      /Path/to/Variable.txt
    Resource      /Path/to/loginkeywords.txt
    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Whether Login
      Open Login Page
      Enter Username  @{username}[0]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Successful  @{username}[0]


    *** Keywords ***
    Whether Login
      ${status}=  Call Selenium Api  is_element_present  link=登出 (将是否存在登出按钮的结果当成变量存储起来)
      Run Keyword If  ${status} == True  Click  link=登出 (以变量为条件进行判断)
    Open Login Page (移除)
      Click Link  link=登录
    Enter Username  [Arguments]  ${username}
      Input Text  uname  ${username}
    Enter Password  [Arguments]  ${password}
      Input Text  pwd  ${password}
    Click the Login Button
      Click Button  login
    Login Is Successful  [Arguments]  ${username}
      Page Should Contain  ${username}
    Login Is Failed  [Arguments]  ${username}
      Page Should Not Contain  ${username}
  • Automation One By One - Robot Framework - Keyword/Variable Files

    2011-07-11 16:55:31

    根据Automation One By One - Robot Framework - Test Suite中所描述的百度账号的feature,我们分成了几个test suites,每个suite中都包含了一类test case files.
    比如02_login test suite中包含了成功登录和登录失败2个test case files,比对一下2个test case file中不同的地方:

    • Variable表中的username内容不同
    • Test Case表中最后一句,一个是Login Is Successful, 一个是Login Is Failed
    • Keywords表中Login Is Successful/Login Is Failed定义的内容不同
    1. 除了上述的不同其它的地方基本上一样,为了避免出现脚本复制粘贴重复存在的问题,我们把test files中相同中的地方抽离出来形成独立的文档。
    • 把Variable的内容提取出来形成一个名称为variables.txt的文档。(文档名称可自定义)

    variable.txt文档中的内容:

    ***Variables***
    @{username}=  user1  user2  user3  user4  user5  user6
    @{password}=  password1  password2  password3  ...
    • 把keywords中的内容提取出来形成一个名称为loginkeywords.txt的文档。(文档名称可自定义)

    loginkeywords.txt文档中的内容:

    *** Keywords ***
    Open Login Page
      Click Link  link=登录
    Enter Username  [Arguments]  ${username}
      Input Text  uname  ${username}
    Enter Password  [Arguments]  ${password}
      Input Text  pwd  ${password}
    Click the Login Button
      Click Button  login
    Login Is Successful  [Arguments]  ${username}
      Page Should Contain  ${username}
    Login Is Failed  [Arguments]  ${username}
      Page Should Not Contain  ${username}

    2. 提取成功后,原来的test suites的结构就发生了变化
    • 原来是
    - Baidu                  
     - __init__.txt   
     - 01_register             
      -  01_registerfailed      
      -  02_registersucceed     
      -  __init__.txt
     - 02_login                
      -  01_loginfailed       
      -  02_loginsucceed       
      -  __init__.txt
    • 现在是
    - Baidu                  
     - __init__.txt  
     - variables.txt (注意:因为注册和登录的变量有可能用的是一样的,所以可以将variables提取到最高级别,方便注册和登录的test suite共享)
     - 01_register            
      -  01_registerfailed      
      -  02_registersucceed     
      -  __init__.txt
     - 02_login               
      -  01_loginfailed        
      -  02_loginsucceed
      -  loginkeywords.txt       
      -  __init__.txt

    3. 提取后test case file的内容也有变化,如登录中2个test file 内容的变化
    • 登录失败

    *** Settings ***
    Library       Selenium Library
    Resource      /Path/to/Variable.txt (移除variables表格,导入variables文件)
    Resource      /Path/to/loginkeywords.txt (移除keywords表格,导入loginkeywords文件)

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Open Login Page
      Enter Username  @{username}[4]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Failed  @{username}[4]

    • 登录成功
    *** Settings ***
    Library       Selenium Library
    Resource      /Path/to/Variable.txt (移除variables表格,导入variables文件)
    Resource      /Path/to/loginkeywords.txt (移除keywords表格,导入loginkeywords文件)

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Open Login Page
      Enter Username  @{username}[0]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Successful  @{username}[0]

    总之一句话:Test Case File中只保留初始化所必须的settings及test case中不重复的步骤,越精简越好。即写脚本要有面向对象的思想(封装,继承,多态)
  • Automation One By One - Robot Framework - Test Suite

    2011-07-08 13:55:40

    前面已经介绍了如何进行Test case及Test file的编写,如果要编写的test case有很多,我们需要对这些test case分类,形成不同的test file或是test suite.

    test file:可以整合一个或多个test case,一个test file可以看成是一个test suite.
    test suite: 可以整合一个或多个test file,是比test file更高一级的test suite.

    比如前面介绍的百度账号登录的功能,要想登录肯定是先注册,对注册的测试应该分为成功注册和失败注册2类,登录也要分成登录成功和登录失败2类来测试,那我们就可以把这些测试做一个分类,如下:

    - Baidu                   -- test suite      
     - 01_register              -- test suite 
      -  01_registerfailed       -- test file
      -  02_registersucceed      -- test file
     - 02_login                 -- test suite
      -  01_loginfailed          -- test file
      -  02_loginsucceed         -- test file


    执行脚本时,即可以单独执行某个test file,又可以执行某一类test suite(比如Baidu(执行所有test file),01_register(只执行其下的test file),02_login(只执行其下的test file))

    Automation One By One - Robot Framework - Conception这篇文章中已经介绍了test suite,要想把一个文件夹当成一个test suite必须添加一个初始化文件 (initialization file), 这个初始化文件中存放的是test file中共同的settings或keyword, 所以我们要想真正的执行一个test suite必须将上述的组织结构加入初始人文件 __init__.txt

    - Baidu                   -- test suite  
     - __init__.txt   
     - 01_register              -- test suite 
      -  01_registerfailed       -- test file
      -  02_registersucceed      -- test file
      -  __init__.txt
     - 02_login                 -- test suite
      -  01_loginfailed          -- test file
      -  02_loginsucceed         -- test file
      -  __init__.txt


    __init__.txt中的内容是从原来的test case file中挑出来的共用的东西,比如02_login中的初始化文件内容包括:

    *** Settings ***
    Suite Setup     Start Selenium Server
    Suite Teardown  Stop Selenium Server
    Test Setup      Setup Test
    Test Teardown   Close All Browsers

    *** Keywords ***
    Setup Test
      Open Browser  Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window

    如此以来login test case file中的内容将变成如下


    *** Settings ***
    Library       Selenium Library

    ***Variables***
    @{username}=  user4  user5  user6  ...
    @{password}=  password1  password2  password3  ...

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Open Login Page
      Enter Username  @{username}[0]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Successful @{username}[0]

    *** Keywords ***
    Open Login Page
      Click Link  link=登录
    Enter Username  [Arguments]  ${username}
      Input Text  uname  ${username}
    Enter Password  [Arguments]  ${password}
      Input Text  pwd  ${password}
    Click the Login Button
      Click Button  login
    Login Is Successful  [Arguments]  ${username}
      Page Should Contain  ${username}

    而对于更高级的test suite(BaiDu)中的__init__.txt文件来说,其内容应该是所有的test suites中共存的东西,即
    *** Settings ***
    Suite Setup     Start Selenium Server
    Suite Teardown  Stop Selenium Server

    那下一级的test suite的初始化文件中应该将高一级的test suite中包含的东西移除掉,即

    低一级test suite中的初始化文件

    *** Settings ***
    Test Setup      Setup Test
    Test Teardown   Close All Browsers

    *** Keywords ***
    Setup Test
      Open Browser  Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window




    所以总结一下:

    第一级test suite - BaiDu包含的内容如下:
    • __init__.txt
    *** Settings ***
    Suite Setup     Start Selenium Server
    Suite Teardown  Stop Selenium Server

    • 01_register-test suite
    • 02_login-test suite
    第二级test suite - 01_register包含的内容如下:
    • __init__.txt
    *** Settings ***
    Test Setup      Setup Test
    Test Teardown   Close All Browsers

    *** Keywords ***
    Setup Test
      Open Browser  Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window
    • 01_registerfailed-test case file
    • 02_registersucceed-test case file
    第三级test suite - 02_login包含的内容如下

    • __init__.txt
    *** Settings ***
    Test Setup      Setup Test
    Test Teardown   Close All Browsers

    *** Keywords ***
    Setup Test
      Open Browser  Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window
    • 01_loginfailed-test case file

    *** Settings ***
    Library       Selenium Library

    ***Variables***
    @{username}=  user1  user2  user3  ...
    @{password}=  password1  password2  password3  ...

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Open Login Page
      Enter Username  @{username}[0]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Failed

    *** Keywords ***
    Open Login Page
      Click Link  link=登录
    Enter Username  [Arguments]  ${username}
      Input Text  uname  ${username}
    Enter Password  [Arguments]  ${password}
      Input Text  pwd  ${password}
    Click the Login Button
      Click Button  login
    Login Is Failed  [Arguments]  ${username}
      Page Should Not Contain  ${username}

    • 02_loginsucceed-test case file

    *** Settings ***
    Library       Selenium Library

    ***Variables***
    @{username}=  user1  user2  user3  ...
    @{password}=  password1  password2  password3  ...

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Open Login Page
      Enter Username  @{username}[0]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Successful @{username}[0]

    *** Keywords ***
    Open Login Page
      Click Link  link=登录
    Enter Username  [Arguments]  ${username}
      Input Text  uname  ${username}
    Enter Password  [Arguments]  ${password}
      Input Text  pwd  ${password}
    Click the Login Button
      Click Button  login
    Login Is Successful  [Arguments]  ${username}
      Page Should Contain  ${username}


  • Automation One By One - Robot Framework - Test File

    2011-07-07 11:41:37

    在http://www.51testing.com/index.php?uid-49689-action-viewspace-itemid-240392中,提到了test case file,如果要想执行test case必须把test case放在test file中。

    Test File包含了四部分:Setting(required), Variable(optional), Test Case(required), Keyword(optional)
    • Setting、Test Case是必须的
    • Variable只有当需要变量的时候才有用
    • Keyword只有当standard libraries及external libraries没有,需要将多个keywords组合成另一个功能强大的keyword时才有用。

    1. Setting
    • Setting相当做初始化的工作。
    • Robot Framework本身并不与被测试网站直接交互,而是以test libraries作为驱动,这些libraries可以是第三方的测试工具类库也可以是标准的类库,我们采用的Selenium Library。
    • 要想将一个test case执行起来,必须将Selenium Library导入到test file中;Robot Framework提供了Library setting来完成这项工作。
    • 要导入的library的名称是大小写敏感的。
    • Selenium Library提供了5个默认参数:
      • timeout=5.0, server_host=localhost, server_port=4444, jar_path=None, run_on_failure=Capture Screenshot
    • Example
    1. *** Settings ***
    2. Library       Selenium Library

    如果调用Selenium Library时不提供任何参数,则意味着默认参数刚会被使用。


    1. *** Settings ***
    2. Library       Selenium Library  15  http://192.168.1.15  4455  ${EMPTY}  ${SPACE*2}

    如果调用Selenium Library时提供参数,则以提供的值使用。

    Selenium Library有5个参数,有的参数需要赋新值,有的需要使用默认值,如何处理?
      • 新值直接添加即可。
      • 默认值,可以输入${EMPTY}或是'\ '代表调用时使用默认值。
    2. Test Case - 以登录百度账号为例

    *** Settings ***
    Library       Selenium Library

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Start Selenium Server
      Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window
      Click Link  link=登录
      Input Text  username  user1
      Input Text  normModPsp  password1
      Click Button  xpath=//input[@value=' 登录 ']
      Page Should Contain  user1
      Close Browser
      Stop Selenium Server

    • 第1行,代表从现在开始是Test Case的内容了
    • 第2行,是Test Case的Name
    • 第3行,启动Selenium Server, 因为是代助selenium RC来驱动脚本的。
    • 第4行,打开一个网址,Open Brower关键字提供了3个参数url, browser=firefox, alias=None;url是Required Arguement,其它的参数如果不提供则按默认值;本例子中的brower参数重新赋值了chrome,则相当于用chrome打开定义的url
    • 第5行,最大化打开的浏览器窗口
    • 第6行,单击登录链接
    • 第7、8行,输入用户名及密码
    • 第9行,单击登录按钮
    • 第10行,校验是否登录成功
    • 第11行,关闭浏览器
    • 第12行,停止Selenium Server

    3. Suite Settings
    • Robot Framework执行脚本时的机制是,当某一步失败,接下来的步骤将不会被执行,这样以来,如果上述的脚本任何一步失败,则tear down的task则永远不会被执行,那这个case也就永远不会被关闭,接下来的case也就不会被执行。
    • 为了解决这个问题,我们可以将setup及teardown抽象出来,放在settings下面,这样一个case的某步失败后,Robot Framework会自动执行tearn down task,且下一个Case会在上一个case tear down后仍然可以执行(setup)。
    • 相同的原因,我们也希望打开浏览及关闭浏览器的运作也需要在开始执行case前及case失败后完成,这个愿望是可以通过settting中的Test Setup/Test Teardown setting来实现。
    *** Settings ***
    Library       Selenium Library
    Suite Setup     Start Selenium Server
    Suite Teardown  Stop Selenium Server
    Test Setup      Open Browser  http://www.baidu.com/  chrome
    Test Teardown   Close All Browsers


    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Maximize Browser Window
      Click Link  link=登录
      Input Text  username  user1
      Input Text  normModPsp  password1
      Click Button  xpath=//input[@value=' 登录 ']
      Page Should Contain  user1

    • Suite Setup: Fires once before any tests during the suite are run.
    • Suite Teardown: Fires when all tests in the suite have completed, even when one of them has failed.
    • Test Setup: Fires before each test is executed.
    • Test Teardown: Fires at the end of each test execution, even when the test fails.
    4. Variable - username字段被重复用到2次,一次是登录时的输入,另一次是登录成功的验证,为了防止username发生变化引起的维护成本,我们将原来的常量换成变量。

    *** Settings ***
    Library       Selenium Library
    Suite Setup     Start Selenium Server
    Suite Teardown  Stop Selenium Server
    Test Setup      Open Browser  http://www.baidu.com/  chrome
    Test Teardown   Close All Browsers

    ***Variables***
    @{username}=  user1  user2  user3  ...
    @{password}=  password1  password2  password3  ...

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Maximize Browser Window
      Click Link  link=登录
      Input Text  username  @{username}[0]
      Input Text  normModPsp  @{password}[0]
      Click Button  xpath=//input[@value=' 登录 ']
      Page Should Contain  @{username}[0]


    • 变量的设置应该放置在 ***Variable***
    • 变量命名最好由a-z,A-Z,0-9,_,space组成
    • Variable的作用域:
      • Variable定义在Testcases内容中,则只对该testcase起作用;
      • 如果一个test file中包含了2个test cases,而这2个test cases都需要用到同一个变量,那就需要把这个变量提升至Variables中,这个变量的作用域覆盖到了test file中所有的test cases;
      • 无论是在test case中或test file中的变量,其作用范围只是这个test case或test file;如果这个变量需要被不同的test files或是test suite中共用的,则需要将所有变量存在一个文件中,然后将这个变量文件作为resource导入。
      • 在Settings中,使用Resource setting完成导入的运作。
    ***Settings***
    Resource  /path/to/file.txt

    • Variable有类型有以下几种:
      • Scalar Variable: 只有一个值的变量, 表达式${username}=  user1
      • List Variable: 一个变量中包含多个值,表达式:@{username}=  user1  user2  user3  ...
        • 取得某一个特定值时,用@{NAME}[i]来表达, i索引,从0开始
      • Environment Variable: 操作系统级的变量,属全局变量 表达式%{ENV_VAR_NAME}
    5. User Keyword - 相当于构建一个类或创建一个方法,其目的是使用在Test Case部分中的重复性代码越少越好,这样可以减少脚本维护的成本及提高利用率。
    • 在第3点中,已经把start/stop selenium server,open/close browser抽离出来了, 看看test case table中还有一个Maximize Browser Window,这个Keyword也可以抽离出来放在每个test caser之前完成,那么如何把它也放在Test Setup中呢?
    • 因为setup/teardown setting只能带一个keyword,我们可以借助keywords table来实现

    *** Keywords ***
    Setup Test
      Open Browser  Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window
    • 我们新建了一个Keyword,名称为Setup Test, 这个keyword实现的功能就是打开浏览器并最大化它,所以应该按如下修改脚本:

    *** Settings ***
    Library       Selenium Library
    Suite Setup     Start Selenium Server
    Suite Teardown  Stop Selenium Server
    Test Setup      Setup Test
    Test Teardown   Close All Browsers

    ***Variables***
    @{username}=  user1  user2  user3  ...
    @{password}=  password1  password2  password3  ...

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Click Link  link=登录
      Input Text  username  @{username}[0]
      Input Text  normModPsp  @{password}[0]
      Click Button  xpath=//input[@value=' 登录 ']
      Page Should Contain  @{username}[0]

    *** Keywords ***
    Setup Test
      Open Browser  Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window

    • 上述脚本中有些keywords不是特别易读,为了提高脚本的可读性,我们借助keyword table完成。

    *** Keywords ***
    Setup Test
      Open Browser  Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window
    Enter Username  [Arguments]  ${username}
      Input Text  uname  ${username}
    Enter Password  [Arguments]  ${password}
      Input Text  pwd  ${password}
    Click the Login Button
      Click Button  login
    Login Is Successful
      Page Should Contain  Welcome

    • 优化后的脚本如下:

    *** Settings ***
    Library       Selenium Library
    Suite Setup     Start Selenium Server
    Suite Teardown  Stop Selenium Server
    Test Setup      Setup Test
    Test Teardown   Close All Browsers

    ***Variables***
    @{username}=  user1  user2  user3  ...
    @{password}=  password1  password2  password3  ...

    *** Testcases ***
    Login Should Succeed When the Correct Username and Password are Entered
      Open Login Page
      Enter Username  @{username}[0]
      Enter Password  @{password}[0]
      Click the Login Button
      Login Is Successful @{username}[0]

    *** Keywords ***
    Setup Test
      Open Browser  Open Browser  http://www.baidu.com/  chrome
      Maximize Browser Window
    Open Login Page
      Click Link  link=登录
    Enter Username  [Arguments]  ${username}
      Input Text  uname  ${username}
    Enter Password  [Arguments]  ${password}
      Input Text  pwd  ${password}
    Click the Login Button
      Click Button  login
    Login Is Successful  [Arguments]  ${username}
      Page Should Contain  ${username}

    • 执行这个Test Case时,先将目录定位到这个文件所在的文件夹,然后pybot validlogin.txt 即可。


  • Automation One By One - Robot Framework - Test Case

    2011-07-06 09:41:29

    接续上一篇:http://www.51testing.com/?uid-49689-action-viewspace-itemid-240392

    Robot Framework组成Test Case的要素:
    (1)Action(步骤) - Keyword, 存在2大类Keyword
      • External Libraries
        • SeleniumLibrary
        • SwingLibrary
        • 自定义的libraries - http://robotframework.googlecode.com/svn/tags/robotframework-2.5.6/doc/userguide/RobotFrameworkUserGuide.html#creating-test-libraries
    • User keywords - 通过Keyword table创建,来源是Library keywords, user keywords可以与test case位于同一文件内或是作为单独的一个文件导入到test case的文件中;
      • 这类的Keyword可以想像成把若干Library keywords集合到一起,形成一个新的keyword,新keyword可以完成一步或几步action,其作用类似于编程中的函数或类
    • 在接下来的文章中,主要是针对SeleniumLibrary及BuiltinLibrary进行介绍。

    (2)Argument - 可以是变量或常量
    • Argument与任何语言中的参数作用是一样的,其主要的目的就是为keywords传递数据。
    • 一个keyword可以有0个或多个Arguments。
    • Robot中参数类型有以下几种,这种分类是从Python中借鉴而来的:
      • Required Argument =(Positional Arrangement位置参数)以正确的定位顺序及数量来传入函数的,顺序改变了或数量不正确就不能完成参数的传递
        • Examples: - Login
    ***Test Case***
    TC1  [Documentation]  User can create an account and log in   Create Valid User  username1  password123 
      Attempt to Input with Credentials  username1  password123
      Status Should Be  Logged In
        • username1/password123/Logged In 都是位置参数
        • 有些Keyword也可以没有参数
      • Named Argument(关键字参数)- 如果命名了参数,可以不按顺序传递参数
    ***Test Case***
    TC2 [Documentation]  Named Argument
      Example Keyword  arg2=xxx  arg3=yyy
      • Default Argument - 默认参数是一个已经赋值的关键字参数,如果在函数调用时没有给出值,那么这个赋值就会被传递。

    如:OperatingSystem library中有一个关键字Create File,这个关键字有3个参数 path, content=, encoding=UTF-8
    当使用这个关键字时,path参数必须存在,content及enconding可以不输入,如果不输入则意味着content为空,encoding是UTF-8;如果输入了则按输入的值传递

    ***Test Case***
    TC3 [Documentation]  Default Argument
      Create File  ${TEMPDIR}/empty.txt
      Create File  ${TEMPDIR}/utf-8.txt  Hyvä esimerkki
      Create File  ${TEMPDIR}/iso-8859-1.txt  Hyvä esimerkki  ISO-8859-1
    • Argument可以是常量也可以是变量

    如上述 Create File Keyword, encoding可以是常量如ISO-8859-1,也可是使用变量。
    假设我现在有一个encoding的列表,列表中的值有ISO-8859-1,UTF-8,GBK等等,每次取值都是不一样的。
    在Robot中,我们可以将encodning定义为列表
    @{encoding}=ISO-8859-1  UTF-8  GBK
    当调用Create File这个Keyword时,我们需要传一个变量,即@{encoding}[0],@{encoding}[1]

    • Argument接收的变量可以是一个值,也可是一个对象

    Python及C中均有 *varargs概念
    Robot中的
    Join Paths keyword,其Argument有2个,分别是*pathsbase
    *path意味可以传递任意数量的变量; base只是传递一个变量
    • 以上类型的参数可以同时存在,但是有一定规则:
      • Required Argument(位置参数)必须放在Named Argument(关键字参数)之前
      • Required Argument(位置参数)必须出现在任何一个默认参数之前
      • 如果参数一个对象(即列表或元组),则其必须放在Required Argument(位置参数)和Default Argument(默认参数)之后




  • Automation One By One - Robot Framework - Conception

    2011-07-04 18:17:38

    手动测试过程中,有Test Case(Test Environment,Test Data,Test Steps, Expected Result), Test Suite的概念;软件开发过程上有面向对象的概念(即继承、封装,多态),做自动化测试开发需要具备测试人员的基础知识及程序员的思想。

    1. Test Case - 包含测试数据,一系列的操作步骤及期望值。
    • 写Test Case时,可以使用4种格式,分别是hypertext markup language (HTML), tab-separated values (TSV), plain text, or reStructuredText (reST) formats,本实例中使用plain text的格式。
    • Plain text格式的Test Case使用至少2个空格做为分隔符。
    • 因为空格作为分隔符使用,当需要使用空格原义时,可以使用${EMPTY}变量或是 \ 来进行转义。
    • 组成Test Case的要素
      • 操作步骤(Test Steps)- Action (用Keyword来实现)
      • 期望值(Expected Result)- Argument(可以使用Variable)
      • 测试数据(Test Data)- Argument
      • Example:
    ***Test Cases***
    My Test [Documentation] Example test
    Log ${MESSAGE}

    Another Test
    Should Be Equal ${MESSAGE} Hello, world!

    My Test/Another Test 是Test Case的名字
    Log/Should Be Equal - 是Action(Keywords)
    ${Message}/Hello, world - 是期望值(可以是变量也可以是常量)

    2. 多个Test Cases可以放在同一个文件中,称为test case file。
    • Test Case File的组成
    Table name Used for
    Setting table
    2) Defining metadata for test cases
    Variable table Defining variables
    that can be used elsewhere in the test case
    Test case table Creating test cases from available keywords
    Keyword table Creating user keywords from existing lower-level keywords
    • Examples:
    ***Settings***
    Library OperatingSystem

    ***Variables***
    ${MESSAGE} Hello, world!

    ***Test Cases***
    My Test [Documentation] Example test
    Log ${MESSAGE}
    My Keyword /tmp

    Another Test
    Should Be Equal ${MESSAGE} Hello, world!

    ***Keywords***

    My Keyword [Arguments] ${path}
    Directory Should Exist ${path}

    3. Test Suite - A test suite created from a directory can have similar settings as a suite created from a test case file。
    • 1个或多个具有相同设置的Test Case File可以组成一个Test Suite, 多个Test Suites可以组成另一个更高层的Test Suite。
    • Test Suite中的Test case file可以通过在名称中添加数字来进行排序,如01__some_tests.txt and 02__more_tests.txt。
    • 每层次Test Suites中都应该包含一个初始化文件,即以__init__.txt命名的文件(从Python语法中借鉴而来)。
    • 初始化文件的内容与Test case file文件中的内容基本相同,除了不支持test case table及一部分的settings table内容。
    Table name Used for
    Setting table
    2) Defining metadata for test suite
    Variable table Defining variables
    that can be used elsewhere in the test case
    Keyword table Creating user keywords from existing lower-level keywords

    • 初始化文件主要作用是把具有相同Settings的test case file聚集在一起。
    • 初如化文件中的主要内容:
      • Documentation/Metada
      • Suite Setup, Suite Teardown
      • Test Setup, Test Teardown
      • Force Tags,
      • Default Tags,Test Template,Test Timeout - Not supported
    • Examples:
    ***Settings***
    Library OperatingSystem
    Suite Setup Start Selenium Server
    Force Tags example


    ***Variables***
    ${MESSAGE} Hello, world!


    ***Keywords***

    My Keyword [Arguments] ${path}
    Directory Should Exist ${path}


  • Automation One By One - Robot Framework - Installation

    2011-07-04 13:26:40

    1. 什么是Robot Framework?

    关于Robot Framework的快速入门可参见:http://www.virtuousprogrammer.com/?p=264
    关于Robot Framework的详细说明参见:http://robotframework.googlecode.com/hg/doc/userguide/RobotFrameworkUserGuide.html?r=2.5.7

    2. Robot Framework Architecture



    需要说明的是,Robot Framework本身并不与被测系统有任何直接的交互,它通过Test Libraries或是一些测试工具(如Selenium, AuotIt, Sikuli)作为驱动与被测系统进行交互,相当于是Test Libraries 或测试工具的front-end.

    在接下来的详解中,我们使用Selenium作为Robot Framework的驱动。


    3. 安装

    Robot Framework需要PythonJython环境,具体需要哪个环境主要是看你使用的Test Libraries或是Test Tools的需要,如果使用Sikuli作为测试工具则需要安装Jpython.

    我们选择Python.

    (1). 先安装Python(2.5+),没有可从如下地址下载:http://www.python.org/
    (2). 下载Robot Framework,有几种安装方法可用,参见Installation

       MacOSX安装方式:
    • 转到Robot Framework安装目录
    • python setup.py install
    (3). 下载Selenium Library, 安装方法参见SeleniumLibrary Installation
       MacOSX安装方式:
    • 转到Selenium Library安装目录
    • python setup.py install
    (4). 设置环境变量
      为了能方便地通过命令行运行Robot Framework的脚本,需要设置环境变量,对于Unix类的操作系统环境变量是自动完成的,但是Windows操作系统需要手动设置,具体的方法参见:http://robotframework.googlecode.com/hg/doc/userguide/RobotFrameworkUserGuide.html?r=2.5.7#setting-up-environment
    (5). 验证安装
    • $ pybot --version
      Robot Framework 2.5 (Python 2.6.x on darwin)


     

     


  • Automation One By One - Outline

    2011-07-04 09:28:53

    公司采用的敏捷的开发模式,需求拆分成Userstories, 这些Uerstories划分成若干阶段来完成,每个阶段大概2-3周,一个阶段被称为一个迭代,每个迭代完成完成优先级较高的Userstories.
    敏捷开发模式中的测试,每个迭代中不仅要完成本迭代的Userstories的测试,还要考虑与前一迭代功能的回归测试,随着完成的Userstories越多,Tester要测试的Cases也会越来越多,工作量也会越来越大,而且重复性的工作容易使人麻痹。为了确保已经完成的功能不受新功能或是Bug修复的影响,减轻Tester的工作量,提高工作效率,我们决定针对功能模块引入自动化。
    引用自动化我们考虑了以下几个方面-5W1H:
    1. Why - 上面已经列明了原因,说明了我们要进行自动化测试的目的。
    2. Who - 谁会参与自动化测?参与人员的编程水平如何?
    • 目前公司没有能力招聘高级的自动化测试人员,参与人员主要是原先做黑盒测试的Tester及刚毕业的计算机专业实习生,所以选择的工具要尽量简单易学,上手快。
    • 但同时又要满足我们对自动化测试工具的要求:
      • 即要支持网页测试也要支持手机软件测试。
      • 要有完整的测试框架,能生成详细的测试报告。
        • 每个检查点均有详细的执行结果;
        • 输入每个测试用例的执行结果;
        • 有日志。
      • 要有可使用的IDE。
      • 生成的脚本要具备可维护性,移植性及重复性。
      • 批量、定制执行、自动运行。
      • 异常处理机制。
      • 版本管理。
    3. What - 基于上述种种原因及公司CEO的推荐,我们决定采用Robot Framework, 网址参见:http://code.google.com/p/robotframework/
    4. When - 什么时候引入自动化?
    • 因为自动化测试团队的能力有限,我们还没有办法做到测试驱动开发,所以我们选定在下个迭代完成上个迭代的自动化开发工作,即落后一迭代开始自动化。
    • 对于安全或性能自动化测试最好是在有稳定的版本后再进行。

    5. Which/Where - 自动化测试的范围?
    • 功能、性能、安全、单元测试。
    • 稳定但重复性比较高的。
    • 关键功能点
    • 手动测试很难办到
    • 。。。
    6. How - 接下来的几篇文章将着重进行介绍如何应用Robot Framework进行自动化测试。

  • 如何组织策划一场测试?

    2011-04-25 16:58:19

    黑盒软件测试工程师通常被划分为初级测试工程师,测试工程师,测试组长,测试经理。

    初级测试工程师一般需要具备设计简单CASE,执行CASE,报BUG能力,这些能力在实践6个月-1年后应该就能完全掌握。

    比较难的是从初级测试工程师到测试工程师的跨越,测试工程师除了能高效的执行CASE和报BUG外,还需要能设计覆盖在较全的TEST CASE和提供比较全面的测试报告,更重要的是需要具备组织策划一场测试的能力。

    更难的是从测试工程师到测试组长的过渡,测试组长除了具备上述的所有能力外,还需要有组织测试流程和抽象测试规范的能力,需要从更高的角度看问题,而不是仅仅关注某一个项目的好坏。

    当然测试经理的能力要求的就更高了,它要求的就不仅仅是测试流程了,更重要的是规范整个公司的流程。

    在一家小型的软件公司待了4年,从最初级的测试工程师到现在的测试组长,我经历了所有这些成长期的痛苦。

    今天主要介绍一下如何做好一个测试工程师,并组织和策划一场测试。

    1.先要了解一个公司的测试流程和开发流程,比如我们单位采用的敏捷迭代开发的流程,整个项目工程划分成若干小迭代来完成,每个迭代都走一个小的瀑布模型。

    2.想一想测试流程如何与开发流程搭配进行。
    • 每个迭代开始前
      • 要保证有稳定的User Stories,且这些User Stories是通过专家和项目所有人审核通过的,这样才能保证测试和开发都是走在正确的道路上。
      • 要有测试计划和测试策略。
        • 定义测试范围:比如普通WEB APP测试可能覆盖功能测试,性能测试,安全性测试,安装卸载测试,Broken Link,兼容性测试等等,根据项目不同而不同。
        • 定义测试策略:
          • 是否需要自动化,分别使用哪些工具。
          • 使用什么样的方法设计测试用例。
          • 测试类型在不同阶段的应用。
            • 在每个迭代可能有若干Builds分别Cover不同的功能点,这时候需要针对每个Build进行功能的覆盖。
            • 在所有的功能均完成后,需要对这个迭代所有的Builds进行一次整体的回归。
            • 如果再发生问题,需要再进行若干次Smoking Test直至可以发布为止。
            • 待部署到正式服务器上之后,要走一次Sanity Testing保证大概的流程是正确的,这块可以用自动化来保证。
            • 等到所有迭代均开发完成,可以还需要对所有迭代的功能进行一次项目级的整体回归。
            • 性能/安全性测试是不是在所有迭代的功能完成后进行?
          • 需要有适当的CASE Review来提高测试覆盖率。
          • Bug管理的流程,以方便及时的跟踪和管理。
          • 是否达到了可以Release的标准,如Critical Bug <5%等等
        • 按照目前的这种测试策略还存在哪些风险等
        • 测试计划,具体的时间和人员安排,尤其是跟开发协商好提交Builds的时间,保证自己测试时间的充裕。
    • 迭代开始后,测试和开发2条分别开展工作,主要说测试的工作
      • 设计TEST CASE时应该按照相关的规范,这个是由测试组长来定义的,是考核CASE质量的依据也是提高CASE覆盖率的方法之一。
      • 执行TEST CASE时,也应该按照相关规范来确定哪些CASE应该执行哪些不应该执行,哪些优先执行,哪些是可选执行的,以下是一个优先级安排,可以根据时间的多少来取舍
        • New Feature
        • Fixed Bug
        • The Modules which have most bugs
        • The Main Functions
      • 提交BUG报告时,也应该按照相关模板来提交,以方便进行BUG审核及统计。
      • 提交测试报告,及时向PL及客户汇报项目的情况,根据在测试策略中确定的标准来判断这个项目是否可以Release.
    • 在一个迭代完成后或一个Milestone后,要如下分析:
        • CASE的覆盖率(数量,哪些Modules自己的覆盖最少,以后要加强)。
        • CASE的执行情况(通过率,发生BUG最多的模块,为接下来的测试策略提供依据,如这些模块会着重测试等)。
        • 有效BUG的数量,分布及优先级,通过分析这些BUG得出开发方面存在什么问题,比如对于重复发生的BUG需要开发这边增加UT等。
        • 客户报告的BUG的数量、分布及比率,并分析漏掉这些BUG原因是什么,为接下来的测试策略提供依据,如这些内容会加强测试及覆盖等。
          • 需求理解错误
          • 不细心,CASE有覆盖但未纳入TEST SUITE
          • CASE的覆盖率不全
          • 测试方法不正确
          • 测试数据不充分
          • 性能/安全方面遗漏
          • 易用性方面
    • 根据上面的一些细节可以得出如下流程图:






701/41234>
Open Toolbar