我在Robot Framework中看到了很多“可嵌套”For循环,主要是创建一个带有For循环的关键字,然后在另一个For循环中调用该关键字。我使用Python 2.7.13创建了一个可嵌套的For循环,但由于它主要使用运行关键字语法,因此我无法使用Robot Framework样式语法创建变量(例如${variable_name}= My Keyword
)。对于记录,这是在BuiltIn Robot Framework库下运行关键字,它使用以下语法:
Run Keywords Keyword1 arg11 arg12 AND Keyword2 arg21 arg22
同样地,它可以这样写:
Run Keywords Keyword1 arg11 arg12
... AND Keyword2 arg21 arg22
它通常不支持在其中创建变量。但是,我使用Run关键字作为可嵌套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的新关键字及其合作伙伴将内部变量分配给关键字。但是,这会根据我的喜好稍微改变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循环是两个完全不同的对象。一些关键字被编码,以便他们可以使用其他关键字(如“运行关键字”),但For Loops不会以这种方式编码。如果有人能找到一种方法来改变我的For循环的语法,那么最终结果将更加直观地用于刚刚来自Robot Framework的人。
为了清楚起见,如果从示例中不清楚,我可以使用来自Test Case和For Loop外部的Robot Framework变量。我问的是在For Loop本身创建它们。
答案 0 :(得分:1)
我终于明白了。这是更正后的代码。
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
为了对此解决方案添加一些说明,我不需要变量采用特定格式。如果您希望更准确地指定变量是什么而没有歧义,它可以是,但它不是必需的。我宁愿在这种程序中留下更多选择。