变量的高级特性

扩展变量语法

扩展的变量语法支持获取变量对象的属性值(例如, ${object.attribute}), 甚至还可以执行对象的方法(例如, ${obj.getName()}). 这种语法对标量和列表都可用, 但是大部分时候还是用于前者.

变量扩展语法是一个强大的特性功能, 但是应该谨慎使用. 获取变量的属性一般没有问题, 相对来说, 使用一个变量来保存拥有多个属性的对象总好于使用多个变量. 不过另一方面, 调用对象的方法(特别是方法还需要参数的时候)会使得测试数据变得复杂难懂. 如果必须这么做, 建议将调用方法的代码移到测试库中去做.

下面的例子展示了使用变量扩展语法的大多数场景. 首先假定我们有如下的 变量文件 和测试用例:

  1. class MyObject:
  2.  
  3. def __init__(self, name):
  4. self.name = name
  5.  
  6. def eat(self, what):
  7. return '%s eats %s' % (self.name, what)
  8.  
  9. def __str__(self):
  10. return self.name
  11.  
  12. OBJECT = MyObject('Robot')
  13. DICTIONARY = {1: 'one', 2: 'two', 3: 'three'}
  1. *** Test Cases ***
  2. Example
  3. KW 1 ${OBJECT.name}
  4. KW 2 ${OBJECT.eat('Cucumber')}
  5. KW 3 ${DICTIONARY[2]}

当上面的测试执行时, 关键字获取到的参数解释如下:

  • KW 1 接收到字符串 Robot
  • KW 2 接收到字符串 Robot eats Cucumber
  • KW 3 接收到字符串 two

扩展的变量语法按照如下的顺序进行解析:

  • 变量首先按照全名进行搜索(因为变量名可包含任意字符),只有在没有匹配的情况下才会继续进行扩展语法的解析.
  • 创建基础变量名称. 从 { 后开始, 直到遇到空格或者非字母字符,这之间的字符就是基础变量的名称. 例如, ${OBJECT.name}${DICTIONARY[2]} 基础变量分别是 OBJECT and DICTIONARY
  • 搜索基础变量是否存在. 如果找不到匹配的变量, 则此处就会抛出异常, 当前测试用例失败.
  • 花括号内的表达式被作为Python表达式来运行.如果因为语法非法或者属性不存在等情况造成运行失败, 此处就会抛出异常, 测试用例失败.
  • 整个扩展变量被表达式运行的结果替代.如果对象是用Java实现的, 扩展的变量语法可以用来获取properties. 即假设有个对象 ${OBJ} 有个方法 getName${OBJ.name} 等价于 ${OBJ.getName()}

上例中的Python对象用Java实现的代码:

  1. public class MyObject:
  2.  
  3. private String name;
  4.  
  5. public MyObject(String name) {
  6. name = name;
  7. }
  8.  
  9. public String getName() {
  10. return name;
  11. }
  12.  
  13. public String eat(String what) {
  14. return name + " eats " + what;
  15. }
  16.  
  17. public String toString() {
  18. return name;
  19. }
  20. }

很多Python标准对象, 包括字符串和数字, 都提供了若干实例方法. 这些方法可以使用扩展变量语法(显式或隐式地)调用. 这样做有时候会很有用, 并减少临时变量的使用, 但是如果过度使用也可能会造成测试数据模糊难懂.

下面的例子展示了几个较好的用法:

  1. *** Test Cases ***
  2. String
  3. ${string} = Set Variable abc
  4. Log ${string.upper()} # Logs 'ABC'
  5. Log ${string * 2} # Logs 'abcabc'
  6.  
  7. Number
  8. ${number} = Set Variable ${-2}
  9. Log ${number * 10} # Logs -20
  10. Log ${number.__abs__()} # Logs 2

虽然在Python代码中推荐使用 abs(number) 替代 number.abs() 的用法, 但是在Robot Framework中 ${abs(number)} 不会生效. 这是因为在变量的扩展语法中, 变量名必须是紧跟着花括号的前端. 不过在测试数据中使用 xxx 方法也是值得商榷的事情, 最好还是将这些逻辑移到测试库中解决.

扩展变量语法对 列表变量 也有效. 例如, 如果一个变量 ${EXTENDED} 被赋值了一个对象, 其中包含属性 attribute 该属性值是一个列表, 则可以使用 @{EXTENDED.attribute} 将该属性当列表变量使用.

扩展的变量赋值

Robot Framework 2.7 版本开始, 可以将 关键字返回值 通过 扩展变量语法 赋值给一个标量变量对象的某个属性.

假设有变量 ${OBJECT} 它的属性值可以按下例中的方式设置:

  1. *** Test Cases ***
  2. Example
  3. ${OBJECT.name} = Set Variable New name
  4. ${OBJECT.new_attr} = Set Variable New attribute

扩展的变量赋值语法按下面的规则进行解析:

  • 被赋值的变量必须是个标量, 至少包含一个点(.). 否则不会触发扩展赋值语法.
  • 如果存在一个全名匹配的变量(例如 ${OBJECT.name}), 则该变量被赋值,不会使用扩展语法.
  • 创建基础变量名称. 从 ${ 后开始, 直到最后一个点,这之间的字符就是基础变量的名称. 例如, ${OBJECT.name}${foo.bar.zap} 基础变量分别是 OBJECT and foo.bar 在第二个例子中, 基础名称也包含了普通的扩展变量语法.
  • 属性名取自最后一个点号直到结尾括号 } 之间的所有字符. 例如, ${OBJECT.name}属性名是 name 如果属性名不是字母或下划线开始的, 并且只包含字母,数字和下划线, 则属性名被认为是非法的, 扩展语法不会生效. 整个变量名称被当作一个名字创建新的变量.
  • 属性名合法则开始匹配基础变量名称. 如果没有找到匹配的变量, 扩展语法不会生效.整个变量名称被当作一个名字创建新的变量.
  • 如果找到的变量是一个字符串或者数字, 则扩展语法不会生效,整个变量名称被当作一个名字创建新的变量. 这是因为在Python中不能给字符串或数字增加新的属性.
  • 如果上述所有规则都满足了, 基础变量的属性值才会被设置.如果由于其它任何原因导致属性设置失败, 将会抛出异常, 测试失败.

注解

不同于普通的使用 关键字的返回值 赋值给局部变量,扩展的赋值语法不限制变量的作用域. 因为这其中没有新变量被创建, 改变的只有已有变量的状态, 该变量可用的作用域内的所有测试用例和关键字都能查看到这个变化.

嵌套变量

变量名可以嵌套使用. 这种情况下, 变量的解析从内往外进行.

例如, 有一个变量 ${var${x}}, ${x} 首先被解析. 如果值为 name, 则最终的变量名变为 ${varname}. 可以有多层嵌套, 不过如果任何一层变量不存在, 整个变量的解析失败.

如下例所示, Do X 取值 ${JOHN HOME}${JANE HOME} 取决于 Get Name 是返回 john 还是 jane 如果返回的是其它值, 则 ${${name} HOME} 解析失败.

  1. *** Variables ***
  2. ${JOHN HOME} /home/john
  3. ${JANE HOME} /home/jane
  4.  
  5. *** Test Cases ***
  6. Example
  7. ${name} = Get Name
  8. Do X ${${name} HOME}