自动化测试 RobotFramework自定义静态测试类库总结
实践环境
win11 家庭中文版
Python 3.9.13
robotframework6.1.1
说明:为了方便的使用robot命令,安装好robotframwork后,修改系统环境,添加robot.exe(PYTHON_HOME/Scripts/robot.exe
)所在路径到系统环境变量path
安装参考连接:https://github.com/robotframework/robotframework/blob/master/INSTALL.rst
docutils-0.21.2-py3-none-any.whl
pip install docutils==0.21.2
robotframework-ride 2.0.8.1
创建测试类库
创建测试库类或者模块
可通过Python模块或者类实现测试类库
类库名称
当某个库被导入时库时使用的测试库的名称与实现它的模块或类的名称相同。例如,如果您有一个Python模块MyLibrary
(即文件MyLibrary.py
),它将创建一个名为MyLibrary
的库。
Python类总是在模块内部。如果实现库的类的名称与模块的名称相同,则Robot Framework允许在导入库时省略类名。例如,MyLib.py
文件中的类MyLib
可以用作名为MyLib
的库。这也适用于子模块,例如,如果parent.MyLib
模块具有类MyLib
,也可以仅使用parent.MyLib
导入。如果模块名称和类名不同,则必须同时使用模块和类名,如mymodule.MyLibrary
或者parent.submodule.MyLib
。
建议:
如果类库名称比较长,推荐给类库取个简单的别名。
为类库设置别名
如果多个关键字具有相同的名称,则必须关键字名称前面加上库名称作前缀。库名称通常来自实现它的模块或类名,但在某些情况下需要更改它:
- 需要使用不同的参数多次导入同一个库。
- 库名称太长,不方便使用。
- 希望使用变量在不同的环境中导入不同的库,但使用相同的名称引用它们。
- 该库的名称具有误导性,或者在其他方面很差。在这种情况下,更改实际名称当然是更好的解决方案。
指定新名称的基本语法是在库名称后面加上文本AS(区分大小写),然后再加上新名称。指定的名称显示在日志中,当使用关键字的全名(LibraryName.Keyword name
)时,必须在测试数据中使用。
*** Settings ***
Library packagename.TestLib AS TestLib
Library ${LIBRARY} AS MyName
使用不同参数多次导入相同类库
*** Settings ***
Library SomeLibrary localhost 1234 AS LocalLib
Library SomeLibrary server.domain 8080 AS RemoteLib
*** Test Cases ***
Example
LocalLib.Some Keyword some arg second arg
RemoteLib.Some Keyword another arg whatever
LocalLib.Another Keyword
为类库提供参数
所有作为类实现的测试类库,都可以接收参数。在Setting
部分,类库名称后面指定这些参数,当Robot Framework创建导入库的实例时,会将这些参数传递给其构造函数。如果作为模块实现的库不能接受任何参数,因此尝试为类库指定参数会导致错误。
类库所需的参数数量与类库的构造函数所接受的参数数量相同。默认值和可变数量的参数与关键字参数的工作方式类似。传递给库的参数以及库名称本身都可以使用变量来指定,因此可以通过命令行进行更改。
*** Settings ***
Library MyLibrary 10.0.0.1 8080
Library AnotherLib ${VAR}
上述使用的类库实现
from example import Connection
class MyLibrary:
def __init__(self, host, port=80):
self._conn = Connection(host, int(port))
def send_message(self, message):
self._conn.send(message)
class AnotherLib:
def __init__(self, environment):
self.environment = environment
def do_something(self):
if self.environment == 'test':
# do something in test environment
else:
# do something in other environments
类库作用范围
作为类实现的库可以具有内部状态,该状态可以通过关键字和传递给库构造函数的参数进行更改。因为状态会影响关键字的实际行为,所以确保一个测试用例中的更改不会意外影响其他测试用例是很重要的。这种依赖关系可能会产生难以调试的问题,例如,当添加新的测试用例时,它们不一致地使用库。
Robot Framework试图保持测试用例彼此独立:默认情况下,它为每个测试用例创建新的测试库实例。然而,这种行为并不总是可取的,因为有时测试用例应该能够共享一个公共状态。此外,所有库都没有状态时,根本不需要创建库的新实例。
测试库可以控制何时使用类属性ROBOT_IBRARY_SCOPE
创建库。此属性必须是字符串,并且可以具有以下三个值:
-
TEST
为每个测试用例创建一个新实例。套件
setup
和套件teardown
共享另一个实例。在Robot Framework 3.2之前,此值为TEST CASE
,但现在建议使用TEST
。因为所有未识别的值都被认为与TEST
相同,所以这两个值都适用于所有版本。出于同样的原因,如果库的目标用于RPA,而非测试时,也可以使用值TASK
。如果未设置ROBOT_LIBRARY_COPE
属性时,默认为TEST
。 -
SUITE
将为每个测试套件创建一个新实例。从测试用例文件创建并包含测试用例的最低级别的测试套件都有自己的实例,而更高级别的套件都有各自的实例用于可能的
setup
和teardown
。在Robot Framework 3.2之前,此值为TEST SUITE
。该值仍然有效,但建议将SUITE
用于面向Robot Framework 3.2及更新版本的库。 -
GLOBAL
在整个测试执行过程中只创建一个实例,它由所有测试用例和测试套件共享。从模块创建的库始终是全局的。
注意:
如果使用不同的参数多次导入同一个库,则每次都会创建一个新的实例,忽略ROBOT_LIBRARY_COPE
配置。
当SUITE
或GLOBAL
作用域与具有状态的库一起使用时,建议库使用一些特殊关键字来清除状态。例如,可以在套件setup
或teardown
中使用此关键字,以确保下一个测试套件中的测试用例可以从已知状态开始。例如,SeleniumLibrary
使用GLOBAL
作用域可以在不同的测试用例中使用同一个浏览器,而无需重新打开它,而且它还具有Close All Browsers
关键字,可以轻松关闭所有打开的浏览器。
使用SUITE
作用域的示例类库:
class ExampleLibrary:
ROBOT_LIBRARY_SCOPE = 'SUITE'
def __init__(self):
self._counter = 0
def count(self):
self._counter += 1
print(self._counter)
def clear_counter(self):
self._counter = 0
类库版本
当使用测试库时,Robot Framework会尝试确定其版本。然后将这些信息写入syslog,以提供调试信息。库文档工具Libdoc也将这些信息写入它生成的关键字文档中。
从属性ROBOT_IBRARY_VERSION
读取版本信息,类似于从ROBOT_LIBARY_som
读取库作用范围。如果ROBOT_IBRARY_VERSION
不存在,则尝试从__version__
属性读取信息。这些属性必须是类或模块属性,这取决于库是作为类还是模块实现的。
使用__version__
的示例模块:
__version__ = '0.1'
def keyword():
pass
创建关键字
什么方法被视为关键字
当使用静态库API时,Robot Framework使用反射来找出库类或模块实现了哪些关键字。默认情况下,它不包括以下划线开头的方法和函数。所有未被忽略的方法和函数都被视为关键字。例如,下面的库实现了单个关键字My keyword
。
class MyLibrary:
def my_keyword(self, arg):
return self._helper_method(arg)
def _helper_method(self, arg):
return arg.upper()
限制public方法成为关键字
自动将所有公有方法和函数视为关键字通常效果良好,但有时候我们不期望这样。例如,当将库实现为类时,可能基类中的方法也被视为关键字,当将库实现为模块时, 当将库实现为模块时,导入到模块名称空间的函数也会自动成为关键字,这些可能不是我们想要的。
本节介绍如何防止方法和函数成为关键字。
基于类实现的类库
当库被实现为类时,可以通过将类属性ROBOT_AUTO_KEYWORDS
设置为False
来告诉Robot Framework不要自动将方法暴露为关键字:
class Example:
ROBOT_AUTO_KEYWORDS = False
当ROBOT_AUTO_KEYWORDS
属性被设置为False
时,仅被使用@keyword 装饰器 显式修饰或以拥有robot_name
属性的方法才会变成关键字。 @keyword
装饰器可以用于给关键字设置 自定义名称, 标签 和 参数类型 )。
from robot.api.deco import keyword
class Example:
ROBOT_AUTO_KEYWORDS = False
@keyword
def this_is_keyword(self):
pass
@keyword('This is keyword with custom name')
def xxx(self):
print('关键字: This is keyword with custom name')
def this_is_not_keyword(self):
pass
除了使用ROBOT_AUTO_KEYWORDS
属性,还可以使用更方便的 @library 装饰器来设置不要自动将方法暴露为关键字
from robot.api.deco import keyword, library
@library
class Example:
@keyword
def this_is_keyword(self):
pass
@keyword('This is keyword with custom name')
def xxx(self):
pass
def this_is_not_keyword(self):
pass
注意:
在ROBOT Framework 3.2中,使用ROBOT_AUTO_KEYWORDS
属性和 @library
装饰器来限制哪些方法成为关键字都是ROBOT Framework 3.2中新增的。
显式指定库实现的关键字的另一种方法是使用dynamic或者 hybrid 库api。
基于模块实现的类库
默认情况下,将库实现为模块时,模块命名空间中的所有函数都将成为关键字。对于导入的函数也是如此。例如,如果将以下模块用作类库,则它将包含关键字 Example Keyword
,此外还包含关键字Current Thread
。
from threading import current_thread
def example_keyword():
print('Running in thread "%s".' % current_thread().name)
避免导入的函数成为关键字的一个简单方法是只导入模块(例如import threading
),并通过模块使用函数(例如threading.current_thread()
)或者,可以在导入时为函数提供一个以下划线开头的别名(例如from threading import current_thread as _current_thread
)。
限制函数成为关键字的一种更明确的方法是使用模块级__all__
属性,Python本身也将其用于类似的目的。如果使用它,则只有该属性列出的函数才可以成为关键字。例如,下面的库只实现了一个关键字Example Keyword
:
from threading import current_thread
__all__ = ['example_keyword']
def example_keyword():
print('Running in thread "%s".' % current_thread().name)
def this_is_not_keyword():
pass
如果类库很大,那么在添加、删除或重命名关键字时维护__all__
属性可能是一项艰巨的任务。显式标记哪些函数是关键字的另一种方法是使用ROBOT_AUTO_KEYWORDS
属性。当该属性设置为False
值时,只有用 [@keyword装饰器显式修饰的函数才会成为关键字。例如,此库也仅实现一个关键字Example Keyword
from threading import current_thread
from robot.api.deco import keyword
ROBOT_AUTO_KEYWORDS = False
@keyword
def example_keyword():
print('Running in thread "%s".' % current_thread().name)
def this_is_not_keyword():
pass
注意:
使用 ROBOT_AUTO_KEYWORDS
限制哪些函数成为关键字是ROBOT Framework 3.2中的一项新功能。
使用@not_keyword
装饰器
模块中的函数和类中的方法可以通过使用@not_keyword
装饰器显式标记为非关键字。当一个库被实现为模块时,这个装饰器也可以用来避免导入的函数变成关键字。
from threading import current_thread
from robot.api.deco import not_keyword
not_keyword(current_thread) # Don't expose `current_thread` as a keyword.
def example_keyword():
print('Running in thread "%s".' % current_thread().name)
@not_keyword
def this_is_not_keyword():
pass
与使用@library
装饰器禁用自动关键字发现或将ROBOT_AUTO_KEYWORDS
设置为False
值相比,使用@not_keyword
装饰器是避免函数或方法成为关键字的完全相反的方法。使用哪一个取决于上下文。
注意:
@not_keyword
是ROBOT Framework 3.2中新增功能。
关键字名称
Similarly both the do_nothing
and doNothing
methods can be used as the Do Nothing keyword in the test data.
将测试数据中使用的关键字名称与方法名称进行比较,以找到实现这些关键字的方法。名称比较不区分大小写,并且会忽略空格和下划线。例如,方法hello
映射到关键字名称Hello
、hello
甚至h e l l o
。类似地,do_nothing
和doNothing
方法都可以用作测试数据中的Do Nothing
关键字。
示例库在MyLibrary.py文件中实现为模块:
def hello(name):
print("Hello, %s!" % name)
def do_nothing():
pass
以下示例说明了如何使用上面的示例库。如果您想自己尝试,请确保库位于模块搜索路径.
*** Settings ***
Library MyLibrary
*** Test Cases ***
My Test
Do Nothing
Hello world
设置自定义名称
可以为关键字暴露一个不同的名称,而不是映射到方法名称的默认关键字名称。这可以通过将方法上的robot_name
属性设置为所需的自定义名称来实现
def login(username, password):
print('login')
login.robot_name = 'Login via user panel'
*** Test Cases ***
My Test
Login Via User Panel ${username} ${password}
相比上述显示设置robot_name
属性,通常最简单的是使用 @keyword 装饰器:
from robot.api.deco import keyword
@keyword('Login via user panel')
def login(username, password):
# ...
使用不带参数的该装饰器不会对暴露的关键字名称产生影响,但仍会设置robot_name
属性。这允许标记方法为关键字,但不会真的更改关键字名称。即使方法名称本身以下划线开头,具有robot_name
属性的方法也会创建关键字。
设置自定义关键字名称还可以使库关键字使用嵌入式参数 语法接收参数。
关键字标签(tag)
类库关键字和用户关键字 可以拥有标签。可以通过在方法上设置 robot_tags
属性来定义类库关键字的标签:
from robot.api.deco import keyword
@keyword(tags=['tag1', 'tag2'])
def login(username, password):
# ...
@keyword('Custom name', ['tags', 'here'])
def another_example():
# ...
Another option for setting tags is giving them on the last line of keyword documentation with Tags:
prefix and separated by a comma. For example:
设置tag的另一个方法是在关键字文档的最后一行添加标签:Tags:
打头,以逗号分隔的标签名称。
例如
def login(username, password):
"""Log user in to SUT.
Tags: tag1, tag2
"""
# ...
关键字参数
使用静态和混合(hybrid)API,可以直接从实现关键字的方法获得关键字需要多少个参数的信息。使用 动态库API (https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#dynamic-libraryapi)有其他方式来共享这些信息,因此本节与它们无关。
最常见也是最简单的情况是关键字需要确切数量的参数,在这种情况下,该方法只需接受这些参数。例如,一个实现不带参数的关键字方法也不接收参数,一个实现只带一个参数的关键字实现方法也接收一个参数,依此类推。
接收不同数量的参数的关键字示例
def no_arguments():
print("Keyword got no arguments.")
def one_argument(arg):
print("Keyword got one argument '%s'." % arg)
def three_arguments(a1, a2, a3):
print("Keyword got three arguments '%s', '%s' and '%s'." % (a1, a2, a3))
关键字默认值
关键字使用的某些参数具有默认值,这通常很有用。
在Python中,一个方法总是只有一个实现,并且可能的默认值在方法定义中指定。下面展示了所有Python程序员都熟悉的语法
def one_default(arg='default'):
print("Argument has value %s" % arg)
def multiple_defaults(arg1, arg2='default 1', arg3='default 2'):
print("Got arguments %s, %s and %s" % (arg1, arg2, arg3))
上面的第一个示例关键字可以与零个或一个参数一起使用。如果没有给定任何参数,arg
将取默认值default
。如果有一个参数,arg
将获得该值,并且使用多个参数调用关键字将失败。在第二个示例中,始终需要一个参数,但第二个和第三个参数具有默认值,因此可以将关键字与一到三个参数一起使用。
*** Test Cases ***
Defaults
One Default
One Default argument
Multiple Defaults required arg
Multiple Defaults required arg optional
Multiple Defaults required arg optional 1 optional 2
可变数量的参数 (*varargs
)
Robot Framework还支持接受任意数量参数的关键字。
Python支持接受任意数量参数的方法。相同的语法在库中也有效,如下面的示例所示,它也可以与其他指定参数的方式组合使用:
def any_arguments(*args):
print("Got arguments:")
for arg in args:
print(arg)
def one_required(required, *others):
print("Required: %s\nOthers:" % required)
for arg in others:
print(arg)
def also_defaults(req, def1="default 1", def2="default 2", *rest):
print(req, def1, def2, rest)
*** Test Cases ***
Varargs
Any Arguments
Any Arguments argument
Any Arguments arg 1 arg 2 arg 3 arg 4 arg 5
One Required required arg
One Required required arg another arg yet another
Also Defaults required
Also Defaults required these two have defaults
Also Defaults 1 2 3 4 5 6
自由关键词参数(**kwargs
)
Robot Framework支持Python的**kwargs语法。在本节中,我们将了解如何创建这样的关键字。
如以下示例显示了基本功能:
def example_keyword(**stuff):
for name, value in stuff.items():
print(name, value)
*** Test Cases ***
Keyword Arguments
Example Keyword hello=world # Logs 'hello world'.
Example Keyword foo=1 bar=42 # Logs 'foo 1' and 'bar 42'.
通常,位于关键字最后的所有参数都使用命名语法规则name=value
,并且与任何其他参数都不匹配的,将作为kwargs
传递给关键字。为了避免使用像foo=quux
这样的字面值作为自由关键字参数,必须对其进行转义比如foo\=quux
。
以下示例说明了普通位置参数、可变参数和关键词参数
def various_args(arg=None, *varargs, **kwargs):
if arg is not None:
print('arg:', arg)
for value in varargs:
print('vararg:', value)
for name, value in sorted(kwargs.items()):
print('kwarg:', name, value)
*** Test Cases ***
Positional
Various Args hello world # Logs 'arg: hello' and 'vararg: world'.
Named
Various Args arg=value # Logs 'arg: value'.
Kwargs
Various Args a=1 b=2 c=3 # Logs 'kwarg: a 1', 'kwarg: b 2' and 'kwarg: c 3'.
Various Args c=3 a=1 b=2 # Same as above. Order does not matter.
Positional and kwargs
Various Args 1 2 kw=3 # Logs 'arg: 1', 'vararg: 2' and 'kwarg: kw 3'.
Named and kwargs
Various Args arg=value hello=world # Logs 'arg: value' and 'kwarg: hello world'.
Various Args hello=world arg=value # Same as above. Order does not matter.
仅限关键词参数
从Robot Framework 3.1开始,可以对关键字使用仅命名参数。Python的仅关键字参数提供了这种支持。 仅关键字参数在的*varargs
之后指定,或者在不需要*varargs
时在专用的*
标记之后指定。可能的**kwargs
是在仅关键字参数之后指定的。
例子:
def sort_words(*words, case_sensitive=False):
key = str.lower if case_sensitive else None
return sorted(words, key=key)
def strip_spaces(word, *, left=True, right=True):
if left:
word = word.lstrip()
if right:
word = word.rstrip()
return word
*** Test Cases ***
Example
Sort Words Foo bar baZ
Sort Words Foo bar baZ case_sensitive=True
Strip Spaces ${word} left=False
仅限位置参数
Python支持所谓的仅限位置参数 ,这让指定指定某个参数智能作为位置参数提供,而非命名参数入 name=value
。仅限位置参数在普通参数之前指定,并且在它们之后必须指定特殊的/
标记
def keyword(posonly, /, normal):
print(f"Got positional-only argument {posonly} and normal argument {normal}.")
可以这样使用上面的关键字:
*** Test Cases ***
Example
# Positional-only and normal argument used as positional arguments.
Keyword foo bar
# Normal argument can also be named.
Keyword foo normal=bar
如果仅限位置参数与包含等号的值(如example=usage
)一起使用,则不被认为是命名参数语法,即使=
之前的部分与参数名称匹配。此规则仅适用于仅限位置参数在其正确位置使用而没有其他参数在其前面使用名称参数语法的情况。
*** Test Cases ***
Example
# Positional-only argument gets literal value `posonly=foo` in this case.
Keyword posonly=foo normal=bar
# This fails.
Keyword normal=bar posonly=foo
从Robot Framework 4.0开始,完全支持仅限位置参数。将它们用作位置参数也适用于早期版本,但将它们用作命名参数会导致Python方面的错误。
......
更多详细信息请参考官方文档
参考连接
作者:授客
微信/QQ:1033553122
全国软件测试QQ交流群:7156436
Git地址:https://gitee.com/ishouke
友情提示:限于时间仓促,文中可能存在错误,欢迎指正、评论!
作者五行缺钱,如果觉得文章对您有帮助,请扫描下边的二维码打赏作者,金额随意,您的支持将是我继续创作的源动力,打赏后如有任何疑问,请联系我!!!
微信打赏
支付宝打赏 全国软件测试交流QQ群