Execute some action when Spock test fails
Asked Answered
D

4

12

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

Dibranchiate answered 7/5, 2013 at 13:14 Comment(0)
G
11

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
Guardianship answered 7/5, 2013 at 14:20 Comment(2)
What is the rational behind creating a file with the name of the package in the services directory?Herzberg
@Herzberg the file is there so spock knows how to find the class and load it and run it. It's either a "well named file" or "scan whole classpath", so almost everyone goes w/ well-named file.Casimir
H
3

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.)

Hyssop answered 7/5, 2013 at 14:8 Comment(1)
Hi Peter! How would I go about combining these two in order to invalidate and maybe recreate a broken driver instance? In cloud tests if I receive something like WebDriverException: Session [...] was terminated due to TIMEOUT I can intercept and log the error in the listener, but do not have access to driver or a driver factory the cache of which I can invalidate. Any ideas?Beshore
B
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.

Bowerman answered 6/12, 2013 at 13:16 Comment(0)
D
0

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()
    }
}
Dinnerware answered 13/2, 2015 at 1:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.