Selenium 人类可读配置文件的Groovy DSL与Antlr
希望这不是一个愚蠢的问题 对于回归测试,我编写了一个小工具,它使用Selenium打开屏幕,根据数据库(多个表)验证加载的屏幕数据,并生成带有屏幕截图的报告(如果出现错误) 作为一个懒惰的人,我没有为单个用例编写一个类(一个屏幕大约60个用例),而是编写了一个可以接受多个配置文件作为参数的类配置文件指示testcase的流程(步骤)、表单字段的xpath/id与数据库查询的映射、查询等。 这一切都很好,但问题是配置文件是XML。邻近的项目感兴趣,并希望使用该工具,我希望他们能够轻松理解该工具,并根据自己的需要进行定制。在我看来,XML在这里是不合适的。此外,对于许多使用相同屏幕进行不同用例组合的测试用例,屏幕表单字段和数据库列之间的映射是相同的。如果可以继承而不是复制内容,那就太好了 所以,我希望写一个小的DSL,它是这样的Selenium 人类可读配置文件的Groovy DSL与Antlr,selenium,groovy,antlr,dsl,Selenium,Groovy,Antlr,Dsl,希望这不是一个愚蠢的问题 对于回归测试,我编写了一个小工具,它使用Selenium打开屏幕,根据数据库(多个表)验证加载的屏幕数据,并生成带有屏幕截图的报告(如果出现错误) 作为一个懒惰的人,我没有为单个用例编写一个类(一个屏幕大约60个用例),而是编写了一个可以接受多个配置文件作为参数的类配置文件指示testcase的流程(步骤)、表单字段的xpath/id与数据库查询的映射、查询等。 这一切都很好,但问题是配置文件是XML。邻近的项目感兴趣,并希望使用该工具,我希望他们能够轻松理解该工具,并
open application
load editClient window
switchTo generalTab
verify generalTab{
if dataValidFor clientName then addInfoToReport else addErrorToReport
if dataValidFor clientAddress then addInfoToReport else addErrorToReport
if confidentialData visible then addInfoToReport else addErrorToReport
}
...
...
你明白了我计划做的就是在后台将DSL转换为Java(或Groovy,如果需要的话)方法调用。我知道,对于像Antlr这样的强大库来说,需求并没有那么大。然而,我在Groovy方面的经验非常有限,我甚至不知道它在Groovy中是否可行
我提到这个,它看起来很神奇。然而,我担心在DSL中包含块的能力,如
verify generalTab{
...
}
附言:我在词法分析器和语法分析器(非计算机科学系本科)方面不是专家,但我设法自学了ANTLR,并在几年前的几周内一直在使用它。再说一次,我对Groovy的经验很少。我也不是Groovy DSL方面的专家,但我一直在使用它,我认为您的案例是可行的。但是它很大 书写
verify generalTab { ... }
Groovy似乎决定
verify( generalTab({ ... }) )
所以,一种接近您想要的方法是拦截方法缺失调用(“generalTab”对我来说似乎是一个html组件id,如果我错了,请纠正我)
您将需要:一个verify()
方法和一个methodMissing()
方法
您的if
和else
s。。。嗯,我们能在时用它换成吗?只是为了避免groovy自己的保留字;-)
if
后面的那些双字让整个事情变得很难看。如果你能用一个点或一个词就更好了
when dataValidFor clientName then addInfoToReport otherwise addErrorToReport
决心
when(dataValidFor).clientName(then).addInfoToReport(otherwise).addErrorToReport
这将是奇怪的分析。如果你能做一些事情,比如:
when dataValidFor('clientName') then addInfoToReport otherwise addErrorToReport
我做了以下工作:
report = [:]
// the closure passed as a parameter to the html component
Closure runningVerification
// the closure that handles adding info to report
Closure addInfoToReport = { Data data ->
report[data] = "[INFO] Field '$data.field' from component '$data.component' valid: $data.valid"
}
// the closure that handles adding errors to report
Closure addErrorToReport = { Data data ->
report[data] = "[ERROR] Field '$data.field' from component '$data.component' valid: $data.valid"
}
/*
* The when() method will receive a data object and returns
* a map to be handled by the 'then' and the 'otherwise' cases
*
* The 'then' and 'otherwise' must passes closures to this method
*/
def when(Data data) {
data.component = runningVerification.binding.htmlComponent
[ then:
{ Closure thenAction ->
if (data.valid) thenAction(data)
[ otherwise:
{ Closure otherwiseAction ->
if (!data.valid) otherwiseAction(data)
}
]
}
]
}
/*
* Handles missing method calls. We need this to keep track of the
* 'generalTab', the HTML component whose tests are being ran against
*/
def methodMissing(String method, args)
{
runningVerification = args[0]
runningVerification.delegate = this
runningVerification.binding.htmlComponent = method // awful
runningVerification()
}
/*
* Verify the status of the validation for html component. The
* argument is useless, it needs to access the report variable in
* the binding
*/
def verify(dataValidation) {
def errors = report.findAll { !it.key.valid }.size()
report.each { println it.value }
print "Result: "
if (errors == report.size()) {
println "Every test failed"
} else if (errors == 0) {
println "Success"
} else {
println "At least one test failed"
}
}
class Data { String component; String field; Boolean valid }
Data dataValidFor(String property) {
new Data(valid: new Random().nextInt() % 2, field: property)
}
Data confidentialData(String property) {
new Data(valid: new Random().nextInt() % 2, field: property)
}
verify generalTab {
when dataValidFor('clientName') then addInfoToReport otherwise addErrorToReport
when dataValidFor('clientCountry') then addInfoToReport otherwise addErrorToReport
when confidentialData('clientId') then addInfoToReport otherwise addErrorToReport
}
它是有效的。它(随机)打印:
它变得很难看。这更像是一个概念的证明。您需要使用BaseScripts、GroovyShell分离类,并将其委托给其他类,等等。您还需要整洁地对其建模,考虑一个报告类等等。但到目前为止,我认为这是可行的。不过相当大
我的阅读建议:
Guillaume Laforge为火星上的机器人展示了一个脚本DSL:
Groovy命令表达式的艺术:
这是我今天发送给groovy列表的一封电子邮件,在我通过JFugue完成了一个供我个人使用的DSL之后:
在github上:
在DSL中,您将遇到的问题是
如果clientname的数据有效,那么因为Groovy已经知道如何处理如果,而它不是这样的:-/PS:您看到了吗?Geb看起来很整洁@tim_yates,但是现在已经开发出了类似于框架(数据验证、报告等)的东西,只要配置成DSL就好了。这就是说,下一次我想用硒做点什么的时候,Geb肯定会是我的首选之一。让我浏览一下Groovy DSL文档,找出问题的确切原因。和往常一样,我可以使用DSL.trade“if”的语法来表示“when”,以避免groovy的“if”语法:-)非常感谢。这给了我一个巨大的开端。说真的。我不得不说,当('clientName')的数据有效时,解决方案,然后是AddInfo报告,否则AddErrorReport
更像是勺子喂我。非常感谢。不客气:-)。你知道,你可以做一些很酷的事情(但只是为了语法上的酷,而不是真正的帮助),比如当clientName有效时做,当clientId隐藏时做。奇数单词更容易解析。如果需要,还可以使用groovy的属性missing(String)
删除'clientName'
中的单引号
[INFO] Field 'clientName' from component 'generalTab' valid: true
[ERROR] Field 'clientCountry' from component 'generalTab' valid: false
[INFO] Field 'clientId' from component 'generalTab' valid: true
Result: At least one test failed