Hi Jeremy.

I agree with Marcin in everything he says. You really should refactor the way he suggested. And as he also said the first time he answered your question, this is a Spock rather than a Geb question.

But for what it is worth, I was a little bit bored just now and wanted to find out if an annotation-driven extension could solve the problem. Actually to quickly hack

  • an annotation @SkipSetup which you can add to any feature method and
  • a SkipSetupExtension extends AbstractAnnotationDrivenExtension<SkipSetup> with a visitFeatureAnnotation() method which gets triggered each time an annotated feature method is intercepted

is a matter of two minutes.

The complication here is the fact that actually nothing specific should happen in the annotated method itself but in the feature's setup() method which gets executed at another time (later, more precisely). So we have to find a way to communicate to the setup() method that we want to skip it. The way I did it is a bit contrived, but easy enough to implement:

  • The first time our annotation-driven extension gets triggered (i.e. the first time Spock finds a feature method annotated by @SkipSetup), we create a special method interceptor which only intercepts setup() methods.
  • We add that interceptor instance to the SpecInfo and also save a reference to it in the SkipSetupExtension in order to avoid creating a new interceptor each time we meet a @SkipSetup annotation. We only want to do that once.
  • The interceptor itself gets a List<String> skippedFeatures property which we update with each newly found feature method name carrying the marker annotation.
  • During runtime the interceptor's interceptSetupMethod() method checks if it finds the current feature method name in its skippedFeatures list. Only if it does not, it proceeds to the setup method, otherwise it skips by just doing nothing.

Here is the source code for

  • the annotation,
  • the extension,
  • the interceptor and
  • two almost identical test classes (because we want to check if really one extension instance is created per specification so as not to get into trouble with our internal references) in order to check if it works as expected:

package de.scrum_master.testing

import org.spockframework.runtime.extension.ExtensionAnnotation

import java.lang.annotation.Retention
import java.lang.annotation.Target

import static java.lang.annotation.ElementType.METHOD
import static java.lang.annotation.RetentionPolicy.RUNTIME

@Retention(RUNTIME)
@Target(METHOD)
@ExtensionAnnotation(SkipSetupExtension)
@interface SkipSetup {}

package de.scrum_master.testing

import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo

class SkipSetupExtension extends AbstractAnnotationDrivenExtension<SkipSetup> {
SkipSetupMethodInterceptor interceptor

@Override
void visitFeatureAnnotation(SkipSetup annotation, FeatureInfo feature) {
if (!interceptor) {
interceptor = new SkipSetupMethodInterceptor()
feature.spec.addSetupInterceptor interceptor
}
interceptor.skippedFeatures << feature.name
}
}

package de.scrum_master.testing
import org.spockframework.runtime.extension.AbstractMethodInterceptor
import org.spockframework.runtime.extension.IMethodInvocation

class SkipSetupMethodInterceptor extends AbstractMethodInterceptor {
List<String> skippedFeatures = new LinkedList<>()

@Override
void interceptSetupMethod(IMethodInvocation invocation) throws Throwable {
if (!skippedFeatures.contains(invocation.feature.name))
invocation.proceed()
}
}

package de.scrum_master.testing

import spock.lang.Specification

class SkipSetupTestA extends Specification {
def setup() {
println "SkipSetupTestA -> setup"
}

def feature1() {
println "SkipSetupTestA -> feature1"
expect: true
}

@SkipSetup
def feature2() {
println "SkipSetupTestA -> feature2"
expect: true
}

def feature3() {
println "SkipSetupTestA -> feature3"
expect: true
}

@SkipSetup
def feature4() {
println "SkipSetupTestA -> feature4"
expect: true
}
}

package de.scrum_master.testing

import spock.lang.Specification

class SkipSetupTestB extends Specification {
def setup() {
println "SkipSetupTestB -> setup"
}

def feature1() {
println "SkipSetupTestB -> feature1"
expect: true
}

@SkipSetup
def feature2() {
println "SkipSetupTestB -> feature2"
expect: true
}

def feature3() {
println "SkipSetupTestB -> feature3"
expect: true
}

@SkipSetup
def feature4() {
println "SkipSetupTestB -> feature4"
expect: true
}
}

I am also attaching a ZIP file with the source code for your convenience. 😃

Let us complete the picture with the console log output when running both specifications together:


SkipSetupTestA -> setup
SkipSetupTestA -> feature1
SkipSetupTestA -> feature2
SkipSetupTestA -> setup
SkipSetupTestA -> feature3
SkipSetupTestA -> feature4
SkipSetupTestB -> setup
SkipSetupTestB -> feature1
SkipSetupTestB -> feature2
SkipSetupTestB -> setup
SkipSetupTestB -> feature3
SkipSetupTestB -> feature4

I think this is what you wanted, is it not? 
 
Regards
--
Alexander Kriegisch
https://scrum-master.de
 

Marcin Erdmann schrieb am 03.05.2020 19:22 (GMT +07:00):

Jeremy,
 
I thought that you asked this question once before and after searching through the past messages to the list from you it turned out that I was right. I will just link to my reply to your earlier email on the topic: https://groups.google.com/d/msg/geb-user/7vIvuB64CFw/CvcUVnxmFQAJ. Personally, I'd consider the need to skip the setup method on one or two tests a smell as well as an indicator that these tests do not belong in the spec in question and should probably be moved out to a separate spec.
 
Marcin

On Fri, May 1, 2020 at 6:50 PM jc <[email protected]> wrote:
I think a good enhancement would be the ability to skip the setup() for particular tests.  For example I have 20 tests but 1 or 2 of them are slightly different and don't need the setup().  Perhaps a flag of 
setup: false
 
on the method or something like that.  Just throwing the idea out there as I have had several time this has come up for me.  We can easily get around it by just creating a method of what we want and putting it in all the tests except the ones that don't need it but I thought maybe others have come across this.  Just want to throw an enhancement idea out there.
--
You received this message because you are subscribed to the Google Groups "Geb User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/geb-user/306afc9f-0883-4a40-b16c-e786dbfde9a4%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "Geb User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/geb-user/CA%2B52dQQt_i-1EESWL9fjO3ZMB5FwFXCaeFKb46nn4eg_R2oG%2Bw%40mail.gmail.com.

--
You received this message because you are subscribed to the Google Groups "Geb User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/geb-user/20200503162255.48EAC44C06BC%40dd39516.kasserver.com.

<<attachment: SkipSetup.zip>>

Reply via email to