Robot Framework 用户关键字

先看一个小练习:
创建一个RF测试套件,包含下面的一个用例

用例名:
验证当系统中没有课程的时候,是否能成功添加一个课程

前置条件:
系统中没有课程

测试步骤:
添加课程,输入课程名、详情描述、展示次序,点击创建

预期结果:
创建的课程正确显示在下面的课程列表中。
这里为了简化,我们只检查 课程名就可以了

注意:
这个用例的初始化和清除操作,都是需要设置为无课程状态。
需要我们开发一个 python 测试库,使用 selenium 库开发关键字函数 deleteAllCourse , 实现使用 Python 自动点击删除课程按钮。

开发的测试库代码如下,模块名为 st.py

from selenium import webdriver
import  time

def deleteAllCourse():
    driver = webdriver.Chrome()
    driver.implicitly_wait(10)
#登录
    driver.get('http://localhost/mgr/login/login.html')
    driver.find_element_by_id('username').send_keys('auto')
    driver.find_element_by_id('password').send_keys('sdfsdfsdf')
    driver.find_element_by_tag_name('button').click()
#删除
    driver.implicitly_wait(1)
    while True:
        eles = driver.find_elements_by_css_selector('tr>td>button:nth-child(2)')

        if eles:
            eles[0].click()
            driver.find_element_by_css_selector('.modal-footer  .btn-primary').click()
            time.sleep(1)
        else:
            break
    driver.implicitly_wait(10)
    driver.quit()

测试套件

*** Settings ***
Library  SeleniumLibrary
Library  st
Library  Collections

*** Test Cases ***
测试1
    [Setup]  deleteAllCourse
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  auto
    INPUT TEXT  id=password  sdfsdfsdf
    click element  tag=button

    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  初中化学
    input text  css=*[ng-model="addData.desc"]  初中化学描述
    input text  css=*[ng-model="addData.display_idx"]  5
    click element  css=*[ng-click="addOne()"]
    sleep  1

    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["初中化学"]

    close browser
    [Teardown]  deleteAllCourse

我们测试用例1就完成了,比如我接着实现第二个测试用例,我需要测它的边界值,比如我创建课程名最小是一个字符,最长是100个字等等。。那我现在要测试边界值里面只包含一个字符,假设初中化学,那我应该实现测试用例?假如说我有一个测试2。

*** Settings ***
Library  SeleniumLibrary
Library  st
Library  Collections

*** Test Cases ***
测试1
    [Setup]  deleteAllCourse
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  auto
    INPUT TEXT  id=password  sdfsdfsdf
    click element  tag=button

    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  初中化学
    input text  css=*[ng-model="addData.desc"]  初中化学描述
    input text  css=*[ng-model="addData.display_idx"]  5
    click element  css=*[ng-click="addOne()"]
    sleep  1

    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["初中化学"]

    close browser
    [Teardown]  deleteAllCourse

测试2
        [Setup]  deleteAllCourse
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  auto
    INPUT TEXT  id=password  sdfsdfsdf
    click element  tag=button

    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  化
    input text  css=*[ng-model="addData.desc"]  初中化学描述
    input text  css=*[ng-model="addData.display_idx"]  5
    click element  css=*[ng-click="addOne()"]
    sleep  1

    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["化"]

    close browser
    [Teardown]  deleteAllCourse

我把测试用例1拿来改改是不是就完成了。就改了输入课程名的时候,还有检查点检查的时候。

大家想一些,我们这样直接改用例,写起来也不麻烦,都是复制粘贴改下东西就好了,但是这里有个不好的地方,这个代码量太多了而且重复性很高,基本上都是一样的代码。冗余度太高。还有一个,比如说我有某一个地方的代码要修改,假设是某个变量的定位元素的位置不对了,它的 css 属性变了,如果我要改的话假设我有10个用例,是不是这10个地方都需要挨个的去修改。是不是有这样的麻烦。

python 里我们可以用函数去封装某一个动作,后面在用到的时候,只需要调用封装好的函数,如果有需要修改我只需要函数那一个地方,调用的地方完全不用修改。这是 python 里面的用法。Robot 里面也有类似的东西,叫用户关键字

什么是用户关键字 ?

用户关键字是这样定义的,它是在 Robot 里面实现了关键字,我们通常在 Robot 里面。编程语言中函数的概念,RF 中像 编程语言中函数 概念的东西,就是用户关键字,用户关键字就像 RF 中的函数。我们前面说过四种标 Settings,Variables,Test Cases,KeywordsKeywords 表就是定义用户关键字的。

通常的从测试库(也就是python文件)里面提供的关键字,我们叫做库关键字
还可以在RF文件中实现关键字,这种关键字我们称之为用户关键字

  • 用户关键字类似于RF层面的函数,把多个关键字操作组成一个“宏”关键字。

  • 定义好用户关键字后,该测试套件里面就可以使用该用户关键字了,就像使用库关键字一样。

上面的

定义、使用用户关键字

首先我们要创建关键字表,用户关键字的定义和定义一个用例,写法非常的像。首先定义 Keywords 表,这个 Keywords 它里面定义关键字跟我们定义用例一样,也需要一个用例的名字,这里就叫关键字的名字。

*** Keywords ***
loginwebsite
    Open Browser   http://...    chrome
    Set Selenium Implicit Wait  10

我们封装关键字就依据我们的功能来看,我们是把它整体封装成一个关键字,还是把它拆开分成几个相对小一点的关键字。比如说我们把上面的案例全部封装从打开浏览器到最后检查。全部封装成一个关键字,这个关键字可能就叫打开浏览器登录之后创建一个课程然后再检查结果,这样一个整体,整体可以封装,但是大家考虑一下这样复用性就不强了,因为有些做的事情特别固定,必须先登录然后去创建一个科创,然后再去检查,所以我们从功能模块来考虑的话,把它拆开会好一点,拆开成什么呢?比如说:登录、添加课程、检查的,这样就比较好。

首先我们做一个登录的封装。

*** Keywords ***
loginwebsite
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  auto
    INPUT TEXT  id=password  sdfsdfsdf
    click element  tag=button

我们把登录做成关键字之后,我们可以这样直接替换一下。

*** Settings ***
Library  SeleniumLibrary
Library  st
Library  Collections

*** Keywords ***
loginwebsite
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  auto
    INPUT TEXT  id=password  sdfsdfsdf
    click element  tag=button

*** Test Cases ***
测试1
    [Setup]  deleteAllCourse
    loginwebsite

    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  初中化学
    input text  css=*[ng-model="addData.desc"]  初中化学描述
    input text  css=*[ng-model="addData.display_idx"]  5
    click element  css=*[ng-click="addOne()"]
    sleep  1

    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["初中化学"]

    close browser
    [Teardown]  deleteAllCourse
测试2
    [Setup]  deleteAllCourse
    loginwebsite

    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  化
    input text  css=*[ng-model="addData.desc"]  初中化学描述
    input text  css=*[ng-model="addData.display_idx"]  5
    click element  css=*[ng-click="addOne()"]
    sleep  1

    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["化"]

    close browser
    [Teardown]  deleteAllCourse

这样就可以了,假如说我登录的用户名变了,直接在 *** Keywords *** 里面改一下就好了。但是这边也有一个问题,我这里把用户名写死了,如果我换一个用户名去登录的话,我关键字就不能用了。

我们在 python 里面我们不想写死,我们是通过传参解决的。函数的传参。我们在 Robot 里面也可以有参数定义的概念。我们来看下 Robot 的里面的关键字怎样传参。

  • 参数支持 [Arguments]
*** Keywords ***
loginwebsite
    [Arguments]  ${username}  ${password}
    Open Browser   http://...    chrome
    Set Selenium Implicit Wait  10
    Input Text  id=username   ${username}
    Input Text  id=password   ${password}

使用方法:在测试用例中

loginwebsite    用户名   密码

像刚才那个我们就不用写死了,我们直接在 loginwebsite 写上 [Arguments]

*** Keywords ***
loginwebsite
    [Arguments]  ${username}  ${password}
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  auto
    INPUT TEXT  id=password  sdfsdfsdf
    click element  tag=button

这是配置项,相当于 loginwebsite 所对应的两个参数,那参数名的用户名和密码直接传在这里面就行了。INPUT TEXT id=username ${username} INPUT TEXT id=password ${password} 对应代码:

*** Keywords ***
loginwebsite
    [Arguments]  ${username}  ${password}
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  ${username}
    INPUT TEXT  id=password  ${password}
    click element  tag=button

我们在登录的时候就要带上我们的参数了。例如 loginwebsite auto sdfsdfsdf。对应代码

*** Settings ***
Library  SeleniumLibrary
Library  st
Library  Collections

*** Keywords ***
loginwebsite
    [Arguments]  ${username}  ${password}
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  ${username}
    INPUT TEXT  id=password  ${password}
    click element  tag=button

*** Test Cases ***
测试1
    [Setup]  deleteAllCourse
    loginwebsite  auto  sdfsdfsdf
    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  初中化学
    input text  css=*[ng-model="addData.desc"]  初中化学描述
    input text  css=*[ng-model="addData.display_idx"]  5
    click element  css=*[ng-click="addOne()"]
    sleep  1

    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["初中化学"]

    close browser
    [Teardown]  deleteAllCourse
测试2
    [Setup]  deleteAllCourse
    loginwebsite  auto  sdfsdfsdf
    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  化
    input text  css=*[ng-model="addData.desc"]  初中化学描述
    input text  css=*[ng-model="addData.display_idx"]  5
    click element  css=*[ng-click="addOne()"]
    sleep  1

    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["化"]

    close browser
    [Teardown]  deleteAllCourse

这样写我们的实用性就强一点了。我用不同的用户名和密码,我仍然可以用这个关键字。


接下来封装我们添加课程,我们定义一个关键字 add course,代码就变成这样了:

*** Settings ***
Library  SeleniumLibrary
Library  st
Library  Collections

*** Keywords ***
loginwebsite
    [Arguments]  ${username}  ${password}
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  ${username}
    INPUT TEXT  id=password  ${password}
    click element  tag=button
add course
    [Arguments]  ${name}  ${desc}  ${idx}
    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  ${name}
    input text  css=*[ng-model="addData.desc"]  ${desc}
    input text  css=*[ng-model="addData.display_idx"]  ${idx}
    click element  css=*[ng-click="addOne()"]
    sleep  1
    
*** Test Cases ***
测试1
    [Setup]  deleteAllCourse
    loginwebsite  auto  sdfsdfsdf
    add course  初中化学  初中化学描述  5
    
    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["初中化学"]

    close browser
    [Teardown]  deleteAllCourse
测试2
    [Setup]  deleteAllCourse
    loginwebsite  auto  sdfsdfsdf
    add course  化  化描述  6

    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    should be true  $lessons==["化"]

    close browser
    [Teardown]  deleteAllCourse

最后封装一个获取页面的课程信息。这里先不去实现,大家感觉这个关键字跟前面的两个关键字有什么不同?刚刚的 loginwebsiteadd course 这两个关键字和我们马上要实现的去获取课程列表这样一个关键字有什么地方不一样。

第一前面两个都是有参数的,第三个获取课程列表它没有参数,这是其中一个,还有一个很大的不同,我们获取课程列表需要返回值。我们前面封装的关键字里面是没有返回值的。

python 里面的返回值我们很舒适,直接 return 就行了,那 Robot 里面如果用户有返回值,应该怎么定义呢?

  • 返回值[Return]
*** Keywords ***
GetLessonList
    ${ele}=    xxx  # 此处省略
    ${lessons}=   create list
    :FOR  ${ele}  IN  @{eles}
        \  Log to console   ${ele.text}
        \  Append To List   ${lessons}   ${ele.text}
    [Return]   ${lessons}

使用方法:在测试用例中

${alist}=   GetLessonList

我们来封装一下,最后变成这个样子:

*** Settings ***
Library  SeleniumLibrary
Library  st
Library  Collections

*** Keywords ***
loginwebsite
    [Arguments]  ${username}  ${password}
    OPEN BROWSER  http://localhost/mgr/login/login.html  chrome
    set selenium implicit wait  10

    INPUT TEXT  id=username  ${username}
    INPUT TEXT  id=password  ${password}
    click element  tag=button
add course
    [Arguments]  ${name}  ${desc}  ${idx}
    click element  css=*[ng-click="showAddOne=true"]
    INPUT TEXT  css=*[ng-model="addData.name"]  ${name}
    input text  css=*[ng-model="addData.desc"]  ${desc}
    input text  css=*[ng-model="addData.display_idx"]  ${idx}
    click element  css=*[ng-click="addOne()"]
    sleep  1
get course list
    ${lessons}=  create list
    ${eles}=  get webelements  css=tr>td:nth-child(2)
    :For  ${ele}  in  @{eles}
    \  log to console  ${ele.text}
    \  append to list  ${lessons}  ${ele.text}
    [Return]  ${lessons}

*** Test Cases ***
测试1
    [Setup]  deleteAllCourse
    loginwebsite  auto  sdfsdfsdf
    add course  初中化学  初中化学描述  5
    ${lessons}=  get course list

    should be true  $lessons==["初中化学"]
    close browser
    [Teardown]  deleteAllCourse
测试2
    [Setup]  deleteAllCourse
    loginwebsite  auto  sdfsdfsdf
    add course  化  化描述  6
    ${lessons}=  get course list

    should be true  $lessons==["化"]
    close browser
    [Teardown]  deleteAllCourse

都过我们的封装,测试 log 就简单了很多。

image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容