Search code examples
javatestingspock

Exception handler for unexpected expceptions in Spock


Is there a way to handle unexpected exceptions in Spock? My use-case is to make test failures where exceptions are thrown better human-readable. E.g. when a test fails with an HttpClientErrorException, I would like to have the status code and the body in the printed test output.

e.g.

when:
restTemplate.getForObject(url, String)

then:
noExceptionThrown()

Now the getForObject() call throws an HttpClientErrorException I want an output like this:

Expected no exception to be thrown, but got 'HttpClientErrorException'
  Status-Code: 400
  Body: '{"error": "parameter foo missing"}'

Solution

  • You can write a custom extension to handle these exceptions yourself, you can't use noExceptionThrown() in this case, as this would prevent the exception to leave the feature method.

    import spock.lang.*;
    import java.lang.annotation.*;
    import org.spockframework.runtime.extension.*;
    import org.spockframework.runtime.model.*;
    
    class NiceExceptionsInterceptor implements IMethodInterceptor {
        static final NiceExceptionsInterceptor INSTANCE = new NiceExceptionsInterceptor()
        
        void intercept(IMethodInvocation invocation) throws Throwable {
            try {
                invocation.proceed()
            } catch (SpecialException e) {
                throw new AssertionError("Special Exception happened: "+e.message)
            }
        }
    }    
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target([ElementType.TYPE, ElementType.METHOD])  
    @ExtensionAnnotation(NiceExceptionsExtensions)
    @interface NiceExceptions {
    
    }
    
    class NiceExceptionsExtensions implements IAnnotationDrivenExtension<NiceExceptions> {
        @Override
        void visitSpecAnnotation(NiceExceptions annotation, SpecInfo spec) {
            spec.bottomSpec.allFeatures.featureMethod*.addInterceptor(NiceExceptionsInterceptor.INSTANCE)
        }
    
        @Override
        void visitFeatureAnnotation(NiceExceptions annotation, FeatureInfo feature) {
            feature.featureMethod.addInterceptor(NiceExceptionsInterceptor.INSTANCE)
        }
    }
    
    // ---- Usage DEMO 
    
    class SpecialException extends Exception {
        SpecialException(String message) {
            super(message)
        }
    }
    
    @NiceExceptions
    class ASpec extends Specification {
    
        def "spec level"() {
            when:
                throw new SpecialException("Foo")
            then:
                true
        }
    }
    
    
    
    class BSpec extends Specification {
    
        @NiceExceptions
        def "test level"() {
            when:
                throw new SpecialException("Foo")
            then:
                true
        }
    
    }
    

    Try it in the Groovy Web Console

    You can write you handler logic and extract any information in the catch-block.

            catch (SpecialException e) {
                throw new AssertionError("Special Exception happened: "+e.message)
            }