我在 Robot Framework 中看到过很多“可嵌套”的 For 循环,主要是在内部创建一个带有 For 循环的关键字,然后在另一个 For 循环中调用该关键字。我使用 Python 2.7.13 创建了一个可嵌套的 For 循环,但因为它主要使用 Run Keywords 语法,所以我无法使用 Robot Framework 风格的语法创建变量(例如 ${variable_name}= My Keyword
) .作为记录,这是 BuiltIn Robot Framework 库下的 Run Keywords,它使用以下语法:
Run Keywords Keyword1 arg11 arg12 AND Keyword2 arg21 arg22
等价地,可以这样写:
Run Keywords Keyword1 arg11 arg12
... AND Keyword2 arg21 arg22
它通常不支持在其中创建变量。但是,我使用 Run Keywords 作为可嵌套 For 循环的一部分。这是该关键字的 Python 代码。
from robot.libraries.BuiltIn import BuiltIn
class Loops(object):
def __init__(self):
self.selenium_lib = BuiltIn().get_library_instance('ExtendedSelenium2Library')
self.internal_variables = {}
def for_loop(self, loop_type, start, end, index_var, *keywords):
# Format the keywords
keywords = self._format_loop(*keywords)
# Clean out the internal variables from previous iterations
self.internal_variables = {}
# This is the actual looping part
for loop_iteration in range(int(start), int(end)):
keyword_set = self._index_var_swap(loop_iteration, index_var, *keywords)
# If it's a one-keyword list with no arguments, then I can use the fastest possible keyword to run it
if len(keyword_set) == 1:
BuiltIn().run_keyword(keyword_set)
# If it's a one-keyword list with arguments, then I can use a faster keyword to run it
elif 'AND' not in keyword_set:
BuiltIn().run_keyword(*keyword_set)
# If it's a multiple-keyword list, then I have to use Run Keywords
else:
BuiltIn().run_keywords(*keyword_set)
def _format_loop(self, *keywords):
keywords = list(keywords) # I need to format the keywords as a list.
changed = False # Whether or not I changed anything in the previous iteration.
index = 0 # The item index I'm at in the list of keywords
del_list = [] # The list of items I need to delete
swap_list = [] # The list of items i need to swap to AND for the use of Run Keywords
# For each argument
for x in keywords:
# Format it to a string
x = str(x)
# If the keyword in question happens to be one of the 'Assign Internal Variable' keywords, then I need
# to run it now, not later.
# By splitting it up, I add a little complexity to the code but speed up execution when you're just
# assigning a scalar variable as opposed to having to search through the next few items just to find
# what I know is just going to be the next one.
# So, if it's the simple assignment...
if x.lower() == 'assign internal variable':
# ...run the Assign Internal Variable keyword with the two inputs
BuiltIn().run_keyword(x, *keywords[int(index)+1:int(index)+3])
# If it's the more complicated variable...
elif x.lower() == 'assign internal variable to keyword':
# ...initialize variables...
deliminator_search = 0
k_check = x
# ...search the next few keywords for a deliminator...
while k_check != '\\' and k_check != '\\\\':
deliminator_search = deliminator_search + 1
k_check = keywords[int(index)+deliminator_search]
# ...and run the Assign Internal Variable to Keyword keyword with the found keyword
BuiltIn().run_keyword(x, *keywords[int(index)+1:int(index)+2+deliminator_search])
# If the previous element was not changed...
if not changed:
# If the current item is not the last one on the list...
if x != len(keywords) - 1:
# If the current item is a deliminator...
if x == '\\':
# If the next item is a deliminator, delete this item and set changed to True
if keywords[int(index) + 1] == '\\':
del_list.append(index)
changed = True
# If the next item is not a deliminator...
else:
# If this isn't the first deliminator on the list, swap it to an 'AND'
if index != 0:
swap_list.append(index)
changed = True
# If this deliminator is in position index=0, just delete it
else:
del_list.append(index)
changed = True
# If the current element is not a deliminator, then I don't need to touch anything.
# If the current element is the last one, then I don't need to touch anything
# If the previous element was changed, then I don't need to "change" this one...
elif changed:
changed = False
# ...but if it's a deliminator then I do need to set it up for the inner for loop it means.
if keywords[index] == '\\':
keywords[index] = '\\\\'
index = index + 1 # Advance the index
# These actually do the swapping and deleting
for thing in swap_list:
keywords[thing] = 'AND'
del_list.reverse()
for item in del_list:
del keywords[item]
# I also need to activate my variables for this set of keywords to run.
keywords = self._activate_variables(*keywords)
return keywords
@staticmethod
def _index_var_swap(loop_iteration, index_var, *keywords):
# Format the keywords as a list for iteration
keywords = list(keywords)
index = 0
# For every line in keywords
for line in keywords:
# Replace all instances of the index_var in the string with the loop iteration as a string
keywords[index] = str(line).replace(str(index_var), str(loop_iteration))
index = index + 1
return keywords
def assign_internal_variable(self, variable_name, assignment):
# This keyword works like any other keyword so that it can be activated by BuiltIn.run_keywords
# The syntax for an internal variable is '{{varName}}' where varName can be anything
self.internal_variables[variable_name] = assignment
def assign_internal_variable_to_keyword(self, variable_name, keyword, *assignment):
# This keyword works like any other keyword so that it can be activated by BuiltIn.run_keywords
# The syntax for an internal variable is '{{varName}}' where varName can be anything
self.internal_variables[variable_name] = BuiltIn.run_keyword(keyword, *assignment)
def _activate_variables(self, *keywords):
# Initialize variables
keywords = list(keywords) # Cast keywords as a List
index = 0 # The index of the keyword I'm looking at
# For each keyword
for keyword in keywords:
keyword = str(keyword) # Cast keyword as a String
assignment = False # Whether or not the found variable name is in a variable assignment
for key in self.internal_variables.keys():
key = str(key) # Cast key as a String
# If I can find the key in the keyword and it's not an assignment...
if keyword.find(key) > -1 and not assignment:
# ...replace the text of the key in the keyword.
keywords[index] = keyword.replace(str(key), str(self.internal_variables[key]))
# If the keyword I'm looking at is an assignment...
if keyword.lower() == 'assign internal variable'\
and keyword.lower() != 'assign internal variable to keyword':
# ...then my next keyword is going to definitely be a known variable, so I don't want to touch it.
assignment = True
# If the keyword I'm looking at is not an assignment...
else:
# ...set assignment to False just in case the previous one happened to be an assignment.
assignment = False
index = index + 1 # Advance the index
return keywords # Return the list of keywords to be used in the format loop
如您所见,我的解决方法是创建一个名为 Assign Internal Variable 的新关键字及其伙伴 Assign Internal Variable to Keyword。然而,这改变了 Robot Framework 循环的语法,这对我来说有点太明显了,并且在某种程度上限制了内部变量与外部变量完全分开。 Robot Framework 测试用例中此关键字的作用示例如下:
*** Variables ***
${gold_squadron} = Gold
${red_squadron} = Red
*** Test Cases ***
Test For Loop
For Loop IN RANGE 0 1 INDEX0
... \\ For Loop IN RANGE 1 6 INDEX1
... \\ \\ Assign Internal Variable {{standing_by}} Standing By Red Leader
... \\ \\ Run Keyword If INDEX1 == 1 Log to Console ${red_squadron} Leader Standing By
... \\ \\ Run Keyword Unless INDEX1 == 1 Log to Console ${red_squadron} INDEX1 {{standing_by}}
... \\ For Loop IN RANGE 1 6 INDEX2
... \\ \\ Assign Internal Variable {{standing_by_2}} Standing By Gold Leader
... \\ \\ Run Keyword If INDEX2 == 1 Log to Console ${gold_squadron} Leader Standing By
... \\ \\ Run Keyword Unless INDEX2 == 1 Log to Console ${gold_squadron} INDEX2 {{standing_by_2}}
假设您已将 Loops.py python 文件正确导入为库,该循环将按预期工作。然而,我正在寻找的语法大致如下:
*** Variables ***
${gold_squadron} = Gold
${red_squadron} = Red
*** Test Cases ***
Test For Loop
For Loop IN RANGE 0 1 INDEX0
... \\ For Loop IN RANGE 1 6 INDEX1
... \\ \\ {standing_by}= Standing By Red Leader
... \\ \\ Run Keyword If INDEX1 == 1 Log to Console ${red_squadron} Leader Standing By
... \\ \\ Run Keyword Unless INDEX1 == 1 Log to Console ${red_squadron} INDEX1 {{standing_by}}
... \\ For Loop IN RANGE 1 6 INDEX2
... \\ \\ {standing_by_2}= Standing By Gold Leader
... \\ \\ Run Keyword If INDEX2 == 1 Log to Console ${gold_squadron} Leader Standing By
... \\ \\ Run Keyword Unless INDEX2 == 1 Log to Console ${gold_squadron} INDEX2 {{standing_by_2}}
对于那些不想撕毁 Robot Framework 基本代码的人(不推荐,这很痛苦),For Loops 通常不能嵌套在 Robot Framework 中的原因是因为在基本级别,关键字和 For Loops是两个完全不同的对象。一些关键字被编码以便它们可以使用其他关键字(如“运行关键字”),但 For 循环不是这样编码的。如果有人能想出一种方法来更改我的 For 循环的语法,那将使最终结果更直观地用于刚接触 Robot Framework 的人。
需要说明的是,如果从示例中看不清楚,我可以使用来自测试用例和 For 循环外部的 Robot Framework 变量。我问的是在 For 循环本身中创建它们。
最佳答案
我终于明白了。这是更正后的代码。
from robot.libraries.BuiltIn import BuiltIn
class Loops(object):
def __init__(self):
self.selenium_lib = BuiltIn().get_library_instance('ExtendedSelenium2Library')
self.internal_variables = {}
def for_loop(self, loop_type, start, end, index_var, *keywords):
# Format the keywords
keywords = self._format_loop(*keywords)
# Clean out the internal variables from previous iterations
self.internal_variables = {}
# This is the actual looping part
for loop_iteration in range(int(start), int(end)):
keyword_set = self._index_var_swap(loop_iteration, index_var, *keywords)
# If it's a one-keyword list with no arguments, then I can use the fastest possible keyword to run it
if len(keyword_set) == 1:
BuiltIn().run_keyword(keyword_set)
# If it's a one-keyword list with arguments, then I can use a faster keyword to run it
elif 'AND' not in keyword_set:
BuiltIn().run_keyword(*keyword_set)
# If it's a multiple-keyword list, then I have to use Run Keywords
else:
BuiltIn().run_keywords(*keyword_set)
def _format_loop(self, *keywords):
keywords = list(keywords) # I need to format the keywords as a list.
changed = False # Whether or not I changed anything in the previous iteration.
index = 0 # The item index I'm at in the list of keywords
del_list = [] # The list of items I need to delete
swap_list = [] # The list of items i need to swap to AND for the use of Run Keywords
def _new_variable():
# Default to a variable declaration of 'name='
t = 1
# If my variable declaration is 'name ='
if x[-2:] == ' =':
# Reflect that in the value of t
t = 2
# Count the number of cells until the end of the line
length = self._deliminator_search(index, x, *keywords)
if length == 3 and not BuiltIn().run_keyword_and_return_status("Keyword Should Exist", keywords[index + 1]):
# If I'm assigning a value to my variable
self._assign_internal_variable(x[:-t], str(keywords[index + 1]))
elif length == 3:
# If I'm assigning the result of a keyword without any arguments
self._assign_internal_variable_to_keyword(keywords[index][:-t], str(keywords[index + 1]))
else:
# If I'm assigning the result of a keyword with arguments
self._assign_internal_variable_to_keyword(keywords[index][:-t], keywords[index + 1],
keywords[index + 2:index + (length - 1)])
# Add the variable declaration code to the delete list.
del_list.extend(range(index - 1, index + length))
# For each argument
for x in keywords:
# Format it to a string
x = str(x)
# Assign new variables
if x[-1:] == '=':
_new_variable()
# If the previous element was not changed...
if not changed:
# If the current item is not the last one on the list...
if x != len(keywords) - 1:
# If the current item is a deliminator...
if x == '\\':
# If the next item is a deliminator, delete this item and set changed to True
if keywords[int(index) + 1] == '\\':
del_list.append(index)
changed = True
# If the next item is not a deliminator...
else:
# If this isn't the first deliminator on the list, swap it to an 'AND'
if index != 0:
swap_list.append(index)
changed = True
# If this deliminator is in position index=0, just delete it
else:
del_list.append(index)
changed = True
# If the current element is not a deliminator, then I don't need to touch anything.
# If the current element is the last one, then I don't need to touch anything
# If the previous element was changed, then I don't need to "change" this one...
elif changed:
changed = False
# ...but if it's a deliminator then I do need to set it up for the inner for loop it means.
if keywords[index] == '\\':
keywords[index] = keywords[index]*2
index = index + 1 # Advance the index
# These actually do the swapping and deleting
for thing in swap_list:
keywords[thing] = 'AND'
del_list.reverse()
for item in del_list:
del keywords[item]
# I also need to activate my variables for this set of keywords to run.
keywords = self._activate_variables(*keywords)
return keywords
@staticmethod
def _index_var_swap(loop_iteration, index_var, *keywords):
# Format the keywords as a list for iteration
keywords = list(keywords)
index = 0
# For every line in keywords
for line in keywords:
# Replace all instances of the index_var in the string with the loop iteration as a string
keywords[index] = str(line).replace(str(index_var), str(loop_iteration))
index = index + 1
return keywords
def _assign_internal_variable(self, variable_name, assignment):
# This keyword works like any other keyword so that it can be activated by BuiltIn.run_keywords
self.internal_variables[variable_name] = assignment
def _assign_internal_variable_to_keyword(self, variable_name, keyword, *arguments):
# Uses assign_internal_variable to simplify code.
# BuiltIn().log_to_console(BuiltIn().run_keyword(keyword, *arguments))
self._assign_internal_variable(variable_name, BuiltIn().run_keyword(keyword, *arguments))
# BuiltIn().log_to_console(self.internal_variables[variable_name])
def _activate_variables(self, *keywords):
# Initialize variables
keywords = list(keywords) # Cast keywords as a List
index = 0 # The index of the keyword I'm looking at
# For each keyword
for keyword in keywords:
keyword = str(keyword) # Cast keyword as a String
assignment = False # Whether or not the found variable name is in a variable assignment
for key in self.internal_variables.keys():
key = str(key) # Cast key as a String
# If I can find the key in the keyword and it's not an assignment...
if keyword.find(key) > -1 and not assignment:
# ...replace the text of the key in the keyword.
keywords[index] = keyword.replace(str(key), str(self.internal_variables[key]))
# If the keyword I'm looking at is an assignment...
if keyword.lower() == 'assign internal variable'\
and keyword.lower() != 'assign internal variable to keyword':
# ...then my next keyword is going to definitely be a known variable, so I don't want to touch it.
assignment = True
# If the keyword I'm looking at is not an assignment...
else:
# ...set assignment to False just in case the previous one happened to be an assignment.
assignment = False
index = index + 1 # Advance the index
# NOTE: Replaces the EXACT text, even if it's in another keyword or variable, so be very careful
return keywords # Return the list of keywords to be used in the format loop
@staticmethod
def _deliminator_search(start, keyword, *keywords):
index = 0
while keyword != '\\' and keyword != '\\\\':
keyword = keywords[int(start) + index]
index = index + 1
return index
下面是测试它的代码:
*** Variables ***
${blue_squadron} = Blue
${gold_squadron} = Gold
${green_squadron} = Green
${red_squadron} = Red
*** Test Cases ***
Test For Loop
For Loop IN RANGE 0 1 INDEX0
... \\ For Loop IN RANGE 1 6 INDEX1
... \\ \\ {standing_by}= standing by
... \\ \\ Run Keyword If INDEX1 == 1 Log to Console This is ${red_squadron} Leader standing by
... \\ \\ Run Keyword Unless INDEX1 == 1 Log to Console ${red_squadron} INDEX1 {standing_by}
... \\ For Loop IN RANGE 1 6 INDEX2
... \\ \\ standing_by_2 = standing by
... \\ \\ Run Keyword If INDEX2 == 1 Log to Console This is ${gold_squadron} Leader standing by
... \\ \\ Run Keyword Unless INDEX2 == 1 Log to Console ${gold_squadron} INDEX2 standing_by_2
... \\ For Loop IN RANGE 1 6 INDEX3
... \\ \\ standing_by_3= Get Blue Squadron
... \\ \\ Run Keyword If INDEX3 == 1 Log to Console This is ${blue_squadron} Leader standing by
... \\ \\ Run Keyword Unless INDEX3 == 1 Log to Console ${blue_squadron} INDEX3 standing_by_3
... \\ For Loop IN RANGE 1 6 INDEX4
... \\ \\ standing_by_4 = Get Green Squadron null input
... \\ \\ Run Keyword If INDEX4 == 1 Log to Console This is ${green_squadron} Leader standing by
... \\ \\ Run Keyword Unless INDEX4 == 1 Log to Console ${green_squadron} INDEX4 standing_by_4
*** Keywords ***
Get Blue Squadron
[Return] standing by
Get Green Squadron
[Arguments] ${null_input}
[Return] standing by
要对此解决方案进行一些说明,我不需要变量采用特定格式。它可以,如果你想进一步准确地指定你的变量是什么而没有歧义,但这不是必需的。我宁愿在此类程序中保留更多选项。
关于python - 如何在这个可嵌套的 For 循环中实现 Robot Framework 风格的变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45290821/