Execute some action when Spock test fails

2020-02-11 03:47发布

问题:

I'd like to execute some action when Spock test fails. Specifically, take a screenshot. Is it possible? How to do it?

回答1:

Create a listener class

class ExampleListener extends AbstractRunListener {

  def void error(ErrorInfo error) {
    println "Actual on error logic"
  }
}

then add it to each specification using implementation of IGlobalExtension that is executed for each Spec

class GlobalSpecExtension implements IGlobalExtension {

  @Override
  void visitSpec(SpecInfo specInfo) {
    specInfo.addListener(new ExampleListener())
  }
}

and finally create file named org.spockframework.runtime.extension.IGlobalExtension in your META-INF/services directory (typically it will be under src/test/resources if you are using Maven) with the full name of your IGlobalExtension implementation e.g.

com.example.tests.GlobalSpecExtension


回答2:

The best way to achieve this is to write a (global or annotation-driven) Spock extension that implements and registers an AbstractRunListener. For an example, see OptimizeRunOrderExtension. For how to register a global extension, see the IGlobalExtension descriptor.

There isn't much documentation on extensions because the APIs are still subject to change. If you want to play it safe (and can live with some restrictions), you can implement a JUnit Rule instead.

One problem that you may encounter in both cases is that they don't provide access to the current spec instance. If you need this, you may have to use both an AbstractRunListener (to be notified of the failure) and an IMethodInterceptor (to get hold of the spec instance), both registered by the same extension. (Shouldn't be this hard, but that's what's currently there.)



回答3:

I've managed to do it this way:

class ExampleTest extends GebSpec{

    static boolean success = false

    def setup(){
        success = false
    }

    def cleanup(){
        assert success == true, someAction()
    }

    def someAction(){
    }

    def "TestCase"(){
        expect:
        /*What you expect here*/

        (success = true) != null
    }
}

Before each test case "success" is set to false by the setup() method. At the end of each test case you add the "(success = true) != null" statement. Therefore "success" will only be true if the test case has passed. After each test case the cleanup() method will verify if "success" is true. If it isn't the method someAction() will be called.



回答4:

I can't upvote or comment on user3074543's answer, but it's simpler than creating an extension. I want easy. So I shortened user*'s a little (I don't mean the 1-line methods). You can make the logic simpler by recording failure instead of success, and reduce typing with a done() helper.

class Test extends spock.lang.Specification {
    def fail
    def setup(){ fail = true }
    def done(){ !(fail = false) }
    def cleanup(){ fail && doStuffWhenFail() }
    def 'test things'(){
        expect:
        stuff
        done()
    }
}


标签: testing spock