先看一个小练习:
创建一个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,Keywords。 Keywords 表就是定义用户关键字的。
通常的从测试库(也就是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
最后封装一个获取页面的课程信息。这里先不去实现,大家感觉这个关键字跟前面的两个关键字有什么不同?刚刚的 loginwebsite 和 add 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 就简单了很多。

