对于我们的客户,我们定义了一种特定于域的(自动)测试(脚本)语言来简化测试用例的设置。
ASC 文件中的每个测试由三部分组成:
test <name> - <options> # <-- defines the start of a test and some general options
<testheader> # <-- contains a number of header commands which need to be always filled in
<testbody> # <-- the real test-actions
允许在 a<testheader>
和<testbody>
whitelines 中(使测试用例更好地可读)
为了检查这些 ASC 文件中的定义是否正确,我们制作了一个验证器脚本,它以以下方式检查测试:
# definitions of valid_header_command and valid_test_command not listed here since they themselves are not of importance for the question (just lists with definitions of keywords for those particular sections)
anyotherline = restOfLine - Optional(LineEnd())
test_command = NotAny(OneOfKeywords('if', 'elif', 'else', 'fi') | eot) - (valid_header_command | valid_test_command | anyotherline)
block = Forward()
pre_post_block = Forward()
if_statement = Keyword('if') - vp_expression - eol
then_block = ZeroOrMore(block)
elif_block = Keyword('elif') - vp_expression - eol - ZeroOrMore(block)
else_block = Keyword('else') - eol - ZeroOrMore(block)
fi_statement = Keyword('fi') - eol
conditional_block = if_statement - then_block - ZeroOrMore(elif_block) - Optional(else_block) - fi_statement
block << ( OneOrMore(test_command) | conditional_block ) # pylint: disable=expression-not-assigned
test_implementation = (OneOrMore(block) + eot).setParseAction(self._parseaction_validate_mandatory_header_commands)
test_name = CharsNotIn(' +:!,?;@=()\n\r')
test_options = ( #option-definitios
)
test_definition = Keyword('test') - White(' ') - test_name.addParseAction(self._parseaction_validate_unique_testcase).addParseAction(self._parseaction_reset_per_testcase_data) - test_options - eol
# if we can't find a test_definition, but we can find a line with something on it (so not the end of file), then report an error
testcase = (test_definition - test_implementation) | (restOfLine + ~StringEnd() + LineEnd()).setParseAction(self._parseaction_errorExpectingNextTest)
这适用于最重要的部分,但我们看到当有人if
在<testheader>
命令周围放置一个以防止必须编写 2 个仅在标题中不同的测试用例时,会发生一些奇怪的行为。
经过长时间的考虑,我们决定不允许if
在<testheader>
命令周围使用,因为只有<testheader>
不同的情况非常罕见。
所以现在我们想要改变实现,让它不再允许if
围绕 a 的语句<testheader>
。为此,我们想尝试一种方法,就像我们所做的那样,testcase
在检查和的其余部分之前使用单独的检查test_definition
(定义test
关键字)。(注意:我们必须保持向后兼容,因为几乎从不使用周围的标题部分)。<testheader>
<testbody>
if
我们尝试的是:
将旧的拆分test_command
为一个header_command
部分和一个test_command
部分(仅上面片段中更改的代码):
header_command = NotAny(OneOfKeywords('if', 'elif', 'else', 'fi') | eot) - (valid_header_command)
test_command = NotAny(OneOfKeywords('if', 'elif', 'else', 'fi') | eot) - (valid_test_command | anyotherline)
....
test_implementation = OneOrMore(header_command).setParseAction(self._parseaction_validate_mandatory_header_commands) + OneOrMore(block) + eot
对于<testheader>
命令,此解决方案有效。但是现在它在每个<testbody>
命令上都失败了,因为它们与该header_command
部分不匹配,test_command
如果它在该header_command
部分中失败,我们希望它继续该部分。
再次注意:标题和正文部分中都允许使用空格,因此我们不能将它们用作分隔符。而且我们必须保持向后兼容,因此很难/不可能引入任何其他分隔符。
我们还尝试保留原始代码但向该valid_header_command
部分添加检查,但这不起作用,因为尽管conditional_block
定义是block
它的一部分,但它也包含block
,因此只有当if
语句的部分已经被处理时,它才会处理test_command
包含检查的剩余部分在valid_header_command
。所以在那里处理它太晚了。
最后:我们考虑过更改_parseaction_validate_mandatory_header_commands
方法,但是我们如何确保当失败时它首先转到test_command
before 真正引发错误?因此,我们目前没有进一步遵循这种方法。
我们认为我们最初将旧的testcommand
分成 2 个部分的方法是正确的,但我们已经为此折腾了好几天才能让它发挥作用。所以我们最终在这里寻求帮助。
--> 有没有人知道我们如何确保在我们的验证器发现它不是<testheader>
命令之后,它会<testbody>
在引发错误之前继续检查命令?
注意:实现是在 python 2.7 中使用 pyparsing 2.3.0 完成的
我的一位同事找到了一个可行的解决方案。
他还将块分成包括全部和仅测试命令部分的部分,并用仅测试命令块替换了 if 语句中的块部分。他还添加了一些额外的解析操作:
test_command = NotAny(conditional_construct | eot) - (valid_header_command | valid_test_command | anyotherline)
no_header_command = NotAny(conditional_construct | eot) - (valid_test_command | anyotherline)
block = Forward()
no_header_block = Forward()
if_statement = (Keyword('if') - vp_expression - eol).addParseAction(self._parseaction_in_if_statement)
then_block = ZeroOrMore(no_header_block)
elif_block = Keyword('elif') - vp_expression - eol - ZeroOrMore(no_header_block)
else_block = Keyword('else') - eol - ZeroOrMore(no_header_block)
fi_statement = (Keyword('fi') - eol).addParseAction(self._parseaction_out_if_statement)
conditional_block = if_statement - then_block - ZeroOrMore(elif_block) - Optional(else_block) - fi_statement
block << ( OneOrMore(test_command) | conditional_block ) # pylint: disable=expression-not-assigned
no_header_block << ( OneOrMore(no_header_command) | conditional_block ) # pylint: disable=expression-not-assigned
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句