Suppose we have a Spock specification class like below.
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()
}
}
How can I extract the BDD meta-statements like some scenario
, given
, when
, then
? It is straightforward to process the source file, but I am wondering if it is possible to use reflection to achieve that.
BTW, the motivation to get that information is to facilitate the communication between product owner and developer, such that the product owner can know what behaviors have been implemented and verified without looking at the source code.
Thank you very much.
In the cold hard light of day, there are some issues with this.
I think a much better and more stable solution would be the one in @PeterNiederwieser's comment above.
I'll leave this here for prosperity though, as it's quite a good example of how to generate an AST from some Groovy code as a String...
I don't think reflection will help, as it won't get you the contents of the methods.
You can do it by generating an AST from the source code, and then walking it looking for the nodes of interest.
So given the code in a String like so:
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
}
}'''
You can generate an 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
}
And then you can do something like this (this is probably not the cleanest way of doing it, I was under time constraints) ;-)
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
}
That gives me the output:
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'
Hope it helps...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With