Thanks for sharing, Alexander. It's cool to see how powerful Spock's
extension mechanism is and that it actually allows you to come up with a
mechanism to skip certain lifecycle methods based on annotations.

FWIW, your solution could probably be slightly improved by changing the
extension to be a global one with the following implementation of
IGlobalExtension.visitSpec():

    void visitSpec(SpecInfo spec) {
        def featuresWithSkippedSetup = spec.allFeatures.findAll {
it.getAnnotation(SkipSetup) }
        if (featuresWithSkippedSetup) {
            def interceptor = new
SkipSetupMethodInterceptor(skippedFeatures: featuresWithSkippedSetup*.name)
            spec.addSetupInterceptor(interceptor)
        }
    }

Marcin


On Sun, May 3, 2020 at 5:22 PM Alexander Kriegisch <alexan...@kriegisch.name>
wrote:

> 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
> <http://spockframework.org/spock/docs/2.0-M2/all_in_one.html#_annotation_driven_local_extensions>
> 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 <jeremy.cat...@gmail.com> 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 geb-user+unsubscr...@googlegroups.com.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/geb-user/306afc9f-0883-4a40-b16c-e786dbfde9a4%40googlegroups.com
>> <https://groups.google.com/d/msgid/geb-user/306afc9f-0883-4a40-b16c-e786dbfde9a4%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>
> --
> 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 geb-user+unsubscr...@googlegroups.com.
> 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
> <https://groups.google.com/d/msgid/geb-user/CA%2B52dQQt_i-1EESWL9fjO3ZMB5FwFXCaeFKb46nn4eg_R2oG%2Bw%40mail.gmail.com?utm_medium=email&utm_source=footer>
> .
>
> --
> 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 geb-user+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/geb-user/20200503162255.48EAC44C06BC%40dd39516.kasserver.com
> <https://groups.google.com/d/msgid/geb-user/20200503162255.48EAC44C06BC%40dd39516.kasserver.com?utm_medium=email&utm_source=footer>
> .
>

-- 
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 geb-user+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/geb-user/CA%2B52dQRzDpeqWzOpN-k2o%3DE_dhBGUAQRDZKw%2By3hkB_oHK1eCA%40mail.gmail.com.

Reply via email to