bmuschko / gradle-clover-plugin

Gradle plugin for generating a code coverage report using Clover
Apache License 2.0
74 stars 50 forks source link

MissingMethodException when running Spock tests #156

Closed Zorobay closed 2 years ago

Zorobay commented 2 years ago

When I run the gradle :test task in my Grails project, some tests fail with MissingMethodException, but most tests pass. When I run my tests from IntelliJ (using the same gradle version), none of my tests fail. Disabling clover and running the :test task also makes all my tests pass. I believe there is something in Clover that generates there MissingMethodException.

More specifically, it seems like Clover can't handle my ServiceUnitTests where I'm fetching configs from the Holders class. This is the error message:

11:04:59.419 [DEBUG] [TestEventLogger] dk.erst.plandata.ppram.AdminServiceSpec > hentUdsendelserDerHarLiggetForLangeIKoen FAILED
11:04:59.419 [DEBUG] [TestEventLogger]     org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'adminService': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [dk.erst.plandata.ppram.AdminService]: Constructor threw exception; nested exception is groovy.lang.MissingMethodException: No signature of method: static dk.erst.plandata.ppram.AdminService.$CLV_safeEval_grails_util_Holders$() is applicable for argument types: (Class, Integer) values: [class grails.util.Holders, 12716]
11:04:59.419 [DEBUG] [TestEventLogger]     Possible solutions: $CLV_safeEval_grails_util_Holders$(grails.util.Holders, java.lang.Integer)
11:04:59.419 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1303)
11:04:59.419 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1197)
11:04:59.420 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
11:04:59.420 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
11:04:59.420 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
11:04:59.420 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
11:04:59.420 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
11:04:59.421 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
11:04:59.421 [DEBUG] [TestEventLogger]         at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:847)
11:04:59.421 [DEBUG] [TestEventLogger]         at org.grails.testing.GrailsUnitTest$Trait$Helper.defineBeans(GrailsUnitTest.groovy:99)
11:04:59.421 [DEBUG] [TestEventLogger]         at grails.testing.services.ServiceUnitTest$Trait$Helper.mockArtefact(ServiceUnitTest.groovy:58)
11:04:59.422 [DEBUG] [TestEventLogger]         at org.grails.testing.ParameterizedGrailsUnitTest$Trait$Helper.getArtefactInstance(ParameterizedGrailsUnitTest.groovy:48)
11:04:59.422 [DEBUG] [TestEventLogger]         at grails.testing.services.ServiceUnitTest$Trait$Helper.getService(ServiceUnitTest.groovy:85)
11:04:59.422 [DEBUG] [TestEventLogger]         at dk.erst.plandata.ppram.AdminServiceSpec.hentUdsendelserDerHarLiggetForLangeIKoen(AdminServiceSpec.groovy:23)
11:04:59.422 [DEBUG] [TestEventLogger] 
11:04:59.422 [DEBUG] [TestEventLogger]         Caused by:
11:04:59.422 [DEBUG] [TestEventLogger]         org.springframework.beans.BeanInstantiationException: Failed to instantiate [dk.erst.plandata.ppram.AdminService]: Constructor threw exception; nested exception is groovy.lang.MissingMethodException: No signature of method: static dk.erst.plandata.ppram.AdminService.$CLV_safeEval_grails_util_Holders$() is applicable for argument types: (Class, Integer) values: [class grails.util.Holders, 12716]
11:04:59.422 [DEBUG] [TestEventLogger]         Possible solutions: $CLV_safeEval_grails_util_Holders$(grails.util.Holders, java.lang.Integer)
11:04:59.422 [DEBUG] [TestEventLogger]             at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:184)
11:04:59.422 [DEBUG] [TestEventLogger]             at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
11:04:59.422 [DEBUG] [TestEventLogger]             at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1295)
11:04:59.422 [DEBUG] [TestEventLogger]             ... 13 more
11:04:59.422 [DEBUG] [TestEventLogger] 
11:04:59.422 [DEBUG] [TestEventLogger]             Caused by:
11:04:59.423 [DEBUG] [TestEventLogger]             groovy.lang.MissingMethodException: No signature of method: static dk.erst.plandata.ppram.AdminService.$CLV_safeEval_grails_util_Holders$() is applicable for argument types: (Class, Integer) values: [class grails.util.Holders, 12716]
11:04:59.423 [DEBUG] [TestEventLogger]             Possible solutions: $CLV_safeEval_grails_util_Holders$(grails.util.Holders, java.lang.Integer)
11:04:59.423 [DEBUG] [TestEventLogger]                 at dk.erst.plandata.ppram.AdminService.<init>(AdminService.groovy:11)
11:04:59.423 [DEBUG] [TestEventLogger]                 at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:172)
11:04:59.423 [DEBUG] [TestEventLogger]                 ... 15 more

The AdminService class is defined below:

@Transactional
class AdminService {

    // The below line generates the error!
    private final int maxAntalNulstillinger = Holders?.config?.udsendelse?.maxAntalNulstillinger ?: 20

    List<Udsendelse> hentUdsendelserDerHarLiggetForLangeIKoen() {
        return Udsendelse.findAllByAntalLaasNulstillingerGreaterThanEquals(maxAntalNulstillinger)
    }

    void nulstilUdsendelser(List<Long> ider) {
        Udsendelse.where {
            id in ider
        }.updateAll(antalLaasNulstillinger: 0)
    }

    boolean brugerHarFuldeRettigheder() {
        return SecurityHelper.getRoller()?.contains(Rolle.PLEJ_ERST_PLANDATA_ADMINISTRATOR_UDVIDET.name())
    }
}

And my clover gradle config:

allprojects {
    apply plugin: 'com.bmuschko.clover'

    dependencies {
        clover "org.openclover:clover:${opencloverVersion}"
    }

    clover {
        // Skal ikke behøves men kan ge fejl uden
        licenseLocation = File.createTempFile('clover', '.license').absolutePath

        excludes = ['**/Application.groovy',
                    '**/BootStrap.groovy',
                    '**/UrlMappings.groovy',
                    '**/*GrailsPlugin.groovy',
                    '**/*Mock.groovy',
        ]

        testIncludes = ['**/*Spec.groovy']
        testExcludes = ['**/*.java', '**/domain*.groovy']

        compiler {
            encoding = 'UTF-8'
            additionalGroovycOpts = [configscript: new File("${rootProject.rootDir}/tools/clover/compilestaticConf.groovy").absolutePath]
        }

        report {
            html = true
            xml = true
        }
    }
}

Expected Behavior

If running tests without Clover does not fail, I would expect them to pass when Clover is enabled.

Current Behavior

See above explanation of MissingMethodException. The weird thing is, I have other Grails projects with multiple sub-projects, where this is not an issue. I'm using the same gradle and clover versions and the same gradle setup of clover, but it stil fails.

Context

I'm trying to use Clover to measure line coverage, but it results in some of my tests failing.

Steps to Reproduce (for bugs)

Run tests with .\gradlew.bat test

Your Environment

Alex-Vol commented 2 years ago

If you could create an example that can reproduce this failure perhaps I could try to find out a solution.

My best guess is something in your code is not well supported in the Clover compiler which is not part of this plugin. We use the OpenClover library and issues of that sort are typically rooted in some obscure Groovy problem that the OpenClover Groovy compilation cannot handle. One such issue that was resolved before was the use of CompileStatic annotations which triggered some unexpected failures and a user contributed a fix that allowed the additionalGroovyOpts feature you are making use of to exist. Perhaps this is a similar situation with the @Transactional annotation you need for your class.

Given how little code is in that class and the sparsity of conditional logic might you consider excluding that class from instrumentation and avoiding this issue as a workaround?

Zorobay commented 2 years ago

Thank you very much for your reply. We decided to move to using Jacoco in our project instead, so I can't really recreate this anymore. Thanks anyway :)