Selenium 人类可读配置文件的Groovy DSL与Antlr

Selenium 人类可读配置文件的Groovy DSL与Antlr,selenium,groovy,antlr,dsl,Selenium,Groovy,Antlr,Dsl,希望这不是一个愚蠢的问题 对于回归测试,我编写了一个小工具,它使用Selenium打开屏幕,根据数据库(多个表)验证加载的屏幕数据,并生成带有屏幕截图的报告(如果出现错误) 作为一个懒惰的人,我没有为单个用例编写一个类(一个屏幕大约60个用例),而是编写了一个可以接受多个配置文件作为参数的类配置文件指示testcase的流程(步骤)、表单字段的xpath/id与数据库查询的映射、查询等。 这一切都很好,但问题是配置文件是XML。邻近的项目感兴趣,并希望使用该工具,我希望他们能够轻松理解该工具,并

希望这不是一个愚蠢的问题

对于回归测试,我编写了一个小工具,它使用Selenium打开屏幕,根据数据库(多个表)验证加载的屏幕数据,并生成带有屏幕截图的报告(如果出现错误)

作为一个懒惰的人,我没有为单个用例编写一个类(一个屏幕大约60个用例),而是编写了一个可以接受多个配置文件作为参数的类配置文件指示testcase的流程(步骤)、表单字段的xpath/id与数据库查询的映射、查询等。

这一切都很好,但问题是配置文件是XML。邻近的项目感兴趣,并希望使用该工具,我希望他们能够轻松理解该工具,并根据自己的需要进行定制。在我看来,XML在这里是不合适的。此外,对于许多使用相同屏幕进行不同用例组合的测试用例,屏幕表单字段和数据库列之间的映射是相同的。如果可以继承而不是复制内容,那就太好了

所以,我希望写一个小的DSL,它是这样的

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