Search code examples
groovytraitsspockgrails-3.3

Public groovy method must be public, says the compiler


What is the cause of this error, and how do I fix it?

At first glance, it seems like a defect in the groovy compiler.

:compileIntegrationTestGroovystartup failed:
C:\src\my-project\src\integration-test\groovy\my\project\MyServiceISpec.groovy: 31: The method setup should be public as it implements the corresponding method from interface my.project.MyTrait
. At [31:5]  @ line 31, column 5.
       public void setup() {
       ^

1 error

My grails integration test looks like this:

@Integration
@Rollback
class MyServiceISpec extends Specification implements MyTrait {
    @Autowired
    MyService service

    OtherService otherService = Mock()

    public void setup() {
        myTraithMethod()
        service.otherService = otherService
    }
}

My trait looks like this:

trait MyTrait {
    public void setup() {
        myTraithMethod()
    }

    private myTraitMethod() {
        ...
    }
}

Update Added public keyword to the trait setup method.


Solution

  • I think that the source of that problem is AST, because Spock uses AST transformations and compiles the specification. You can read here http://docs.groovy-lang.org/next/html/documentation/core-traits.html#_compatibility_with_ast_transformations this:

    Traits are not officially compatible with AST transformations. Some of them, like @CompileStatic will be applied on the trait itself (not on implementing classes), while others will apply on both the implementing class and the trait. There is absolutely no guarantee that an AST transformation will run on a trait as it does on a regular class, so use it at your own risk!

    You can solve it by renaming setup() method in the trait on traitSetup() for example and calling it from the specification setup() method like this:

    @Integration
    @Rollback
    class MyServiceISpec extends Specification implements MyTrait {
        @Autowired
        MyService service
        OtherService otherService = Mock()
    
        void setup() {
            service.otherService = otherService
            traitSetup()
        }
    
        def 'some test here'() {
            ...
        }
    }
    
    trait MyTrait {
        void traitSetup() {
            myTraitMethod()
        }
    
        private myTraitMethod() {
            ...
        }
    }