Search code examples
groovylistenerspockdata-driven-tests

In Spock framework, how do I access data table variable from "error" listener?


I have a test with a data table, e.g. like this:

@Unroll
"Basic session start on platform = #platform"() {
    when: "user stats a session on a platform"
    def response = startSession(platform, id) // it does a REST request

    then: "response contains a userId"
    assert verifyUserId(response.userId) // method verifies userId with an internal algorithm and returns true\false if it is valid or not

    where:
    id | platform
    1  | "Facebook"
    2  | "Google"
    3  | "Apple"
}

I have also wrote a listener for errors.

class MyListener extends AbstractListener {
...
   public void error(ErrorInfo error) { ... }
...
}

So that in case an assertion error happens during test execution, the code goes into this "error" method.

The question is, how can I get values of variables from the "where" block from inside the "error" method?

I don't do direct assertions for the data table variables, so ErrorInfo.Exception does not contain them.

I also could not find any other suitable members of the "ErrorInfo" object - I can only find variable names in ErrorInfo.getMethod().getFeature(), but not their values that were there when the error happened.


Solution

  • The main issue here is that ErrorInfo doesn't provide current spec instance, that's why you can't get iteration variables. So I would suggest two ways:

    First one is to use iteration interceptor:

    class ListenForErrorsExtension implements IGlobalExtension {
    
        void visitSpec(SpecInfo specInfo) {
            specInfo.addListener(new Listener())
            specInfo.allFeatures*.addIterationInterceptor(new IMethodInterceptor() {                      
                @Override
                void intercept(IMethodInvocation invocation) {
                    holder.put(invocation.iteration.dataValues)
                    invocation.proceed()
                }
    
            })
        }
    

    The second one was kindly suggested in comments by Opal. You could instantinate not AbstractRunListener, but IRunListener

    class ErrorExtension implements IGlobalExtension{
    ....
        @Override
        void beforeIteration(IterationInfo iteration) {       
            holder.put(iteration.dataValues)
        }
    ....
    }
    

    It gives you access to each iterration, but the main idea is still following:

    You have to create thread safe holder, put iterration data to it BEFORE error happened and then extract.