如何从Spock规范类中提取BDD元语句

时间:2014-02-19 17:06:25

标签: java groovy bdd spock

假设我们有一个Spock规范类,如下所示。

class SomeFeature extends Specification {
    def "some scenario"() {
        given: "some resource"
        def resource = someResource()

        when: "some action is taken"
        someAction()

        then: "some condition must be met"
        true == someCondition()
    }
}

如何提取像some scenariogivenwhenthen这样的BDD元语句?处理源文件很简单,但我想知道是否可以使用反射来实现这一点。

BTW,获取该信息的动机是促进产品所有者和开发者之间的沟通,以便产品所有者可以在不查看源代码的情况下了解已实施和验证的行为。

非常感谢。

1 个答案:

答案 0 :(得分:5)

在严酷的日子里,有一些问题。

  1. 它可能非常脆弱,需要整理以处理不同的案例和格式(展开的描述?辅助方法?参数化描述?)
  2. 即使测试从未执行过,它也会盲目地将所有内容都丢弃。
  3. 我认为更好,更稳定的解决方案将是@ PeterNiederwieser上述评论中的解决方案。

    我会把它留在这里以获得繁荣,因为它是如何从一些Groovy代码生成AST作为String的一个很好的例子......


    我不认为反思会有所帮助,因为它不会为你提供方法的内容。

    您可以通过从源代码生成AST,然后走它寻找感兴趣的节点来实现。

    所以给出了像这样的字符串中的代码:

    def code = '''import spock.*
    
    class SomeFeature extends Specification {
        def "some scenario"() {
            given: "some resource"
            def resource = someResource()
    
            when: "some action is taken"
            someAction()
    
            then: "some condition must be met"
            true == someCondition()
        }
        def "another"() {
           given: 'a value 1'
              def value = 1
           then: '1 == 1'
              value == 1
        }
    }'''
    

    您可以生成AST:

    import org.codehaus.groovy.antlr.*
    import org.codehaus.groovy.antlr.parser.*
    
    def ast = new GroovyRecognizer(
                  new GroovyLexer(
                      new StringReader( code ) ).plumb() ).with { p ->
      p.compilationUnit()
      p.AST
    }
    

    然后你可以做这样的事情(这可能不是最干净的方式,我受时间限制); - )

    while( ast ) {
        if( ast.type == GroovyTokenTypes.CLASS_DEF ) {
            def child = ast.firstChild.nextSibling
            println "Specification '${child.text}'"
            while( child && child.type != GroovyTokenTypes.OBJBLOCK ) {
                child = child.nextSibling
            }
            if( child ) {
                child = child.firstChild
                while( child ) {
                    if( child.type == GroovyTokenTypes.METHOD_DEF ) {
                        def method = child.firstChild
                        println "    Scenario '${method.nextSibling?.nextSibling?.text}'"
                        while( method ) {
                            if( method.type == GroovyTokenTypes.SLIST ) {
                                def statements = method.firstChild
                                while( statements ) {
                                    if( statements.type == GroovyTokenTypes.LABELED_STAT ) {
                                        def label = statements.firstChild
                                        println "        ${label.text.toUpperCase()} '${label.nextSibling?.firstChild?.text}'"
                                    }
                                    statements = statements.nextSibling
                                }
                            }
                            method = method.nextSibling
                        }
                    }
                    child = child.nextSibling
                }
            }
        }
        ast = ast.nextSibling
    }
    

    这给了我输出:

    Specification 'SomeFeature'
        Scenario 'some scenario'
            GIVEN 'some resource'
            WHEN 'some action is taken'
            THEN 'some condition must be met'
        Scenario 'another'
            GIVEN 'a value 1'
            THEN '1 == 1'
    

    希望它有所帮助...