grailsgrails-test

Grails test-app fails while test-app individual unite test works


I have got a Grails 3.3.5 application with several Unit tests. While I run this tests individually they run as expected, but when I run a general test-app the tests fails with this message:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.grails.beans.ConstraintsEvaluator': Cannot resolve reference to bean 'grailsDomainClassMappingContext' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'grailsDatastore': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.datastore.mapping.simple.SimpleMapDatastore]: Constructor threw exception; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<?, ?>]
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:648)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1197)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1099)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
    at org.grails.testing.GrailsUnitTest$Trait$Helper.defineBeans(GrailsUnitTest.groovy:100)
    at org.grails.testing.gorm.spock.DataTestSetupSpecInterceptor.setupDataTestBeans(DataTestSetupSpecInterceptor.groovy:50)
    at org.grails.testing.gorm.spock.DataTestSetupSpecInterceptor.configureDataTest(DataTestSetupSpecInterceptor.groovy:85)
    at org.grails.testing.gorm.spock.DataTestSetupSpecInterceptor.intercept(DataTestSetupSpecInterceptor.groovy:43)
    at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:147)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:129)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'grailsDatastore': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.datastore.mapping.simple.SimpleMapDatastore]: Constructor threw exception; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<?, ?>]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:279)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1197)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1099)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:372)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1177)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1071)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
    ... 36 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.datastore.mapping.simple.SimpleMapDatastore]: Constructor threw exception; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<?, ?>]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:154)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:122)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:271)
    ... 54 more
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<?, ?>]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:324)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:206)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:187)
    at org.springframework.core.env.AbstractPropertyResolver.convertValueIfNecessary(AbstractPropertyResolver.java:266)
    at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:87)
    at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:66)
    at org.springframework.core.env.AbstractPropertyResolver.getProperty(AbstractPropertyResolver.java:169)
    at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:542)
    at org.grails.datastore.mapping.core.connections.AbstractConnectionSources.getConnectionSourceNames(AbstractConnectionSources.groovy:52)
    at org.grails.datastore.mapping.core.connections.InMemoryConnectionSources.<init>(InMemoryConnectionSources.groovy:24)
    at org.grails.datastore.mapping.core.connections.ConnectionSourcesInitializer.create(ConnectionSourcesInitializer.groovy:28)
    at org.grails.datastore.mapping.simple.SimpleMapDatastore.<init>(SimpleMapDatastore.java:117)
    at org.grails.datastore.mapping.simple.SimpleMapDatastore.<init>(SimpleMapDatastore.java:178)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:142)
    ... 56 more

Here's one of the unit test that works as expected when runned individually but fails with the global test:

package my.package

import grails.testing.gorm.DomainUnitTest
import spock.lang.Specification

class CapituloSpec extends Specification implements DomainUnitTest<Capitulo> {

    void "test id cannot be null"() {
        when:
        domain.id = null

        then:
        !domain.validate(['id'])
        domain.errors['id'].code == 'nullable'
    }

    void "test titulo cannot be null or blank"() {
        when:
        domain.titulo = null

        then:
        !domain.validate(['titulo'])
        domain.errors['titulo'].code == 'nullable'

        when:
        domain.titulo = ''

        then:
        !domain.validate(['titulo'])
        domain.errors['titulo'].code == 'blank'
    }

    void "test titulo maxSize"() {
        when: "titulo is 101 characters length"
        domain.titulo = 'a' * 101

        then: "titulo validation fails"
        !domain.validate(['titulo'])
        domain.errors['titulo'].code == 'maxSize.exceeded'

        when: "titulo is 100 characters length"
        domain.titulo = 'a' * 100

        then: "titulo validation succeed"
        domain.validate(['titulo'])
    }

    void "test capitulo ok"() {
        when:
        domain.id = 1
        domain.titulo = "Test"

        then:
        domain.validate()
    }
}

How can I solve this problems and run my tests together and not individually.

EDIT: I forgot to mention that I'm using two datasources, one default to save almost all the application data, and another one to save only 6 domains shared with another application.


Solution

  • Finally I've found a way to avoid the errors and run all the tests together. I must configure the datasource in all the tests using the doWithSpring closure.

    First I made a Trait to configure the datasource using the applicationContext and the datasource name (if not given use the 'default' datasource):

    package my.package
    
    import groovy.transform.CompileStatic
    import org.springframework.context.ConfigurableApplicationContext
    import org.springframework.core.convert.support.ConfigurableConversionService
    import org.springframework.core.convert.converter.Converter
    
    @CompileStatic
    trait MultiDatasourceTest {
    
        def configDatasource(ConfigurableApplicationContext applicationContext, String datasource = "default") {
            System.setProperty("grails.gorm.connections", datasource)
    
            ConfigurableConversionService conversionService = applicationContext.getEnvironment().getConversionService()
            conversionService.addConverter(new StringToMapConverter())
        }
    }
    
    class StringToMapConverter implements Converter<String, Map> {
        @Override
        Map convert(String source) {
            source.split(",").collectEntries({
                [(it):it]
            })
        }
    }
    

    Next I must add the trait to all my tests and call the configDatasource with the application context and the datasource name, if it's not the default one.

    Default datasource:

    package my.package
    
    import grails.testing.gorm.DomainUnitTest
    import org.springframework.context.ConfigurableApplicationContext
    import spock.lang.Specification
    
    class CapituloSpec extends Specification implements DomainUnitTest<Capitulo>, MultiDatasourceTest {
    
        @Override
        Closure doWithSpring() {
            return {
                configDatasource(application.mainContext as ConfigurableApplicationContext)
            }
        }
    
        void "test id cannot be null"() {
            when:
            domain.id = null
    
            then:
            !domain.validate(['id'])
            domain.errors['id'].code == 'nullable'
        }
    
        // More tests
    }
    

    Another datasource:

    package my.package
    
    import grails.testing.gorm.DomainUnitTest
    import org.springframework.context.ConfigurableApplicationContext
    import spock.lang.Specification
    
    class DocumentoTeoSpec extends Specification implements DomainUnitTest<DocumentoTeo>, MultiDatasourceTest {
    
        @Override
        Closure doWithSpring() {
            return {
                configDatasource(application.mainContext as ConfigurableApplicationContext, "sede")
            }
        }
    
        void "test numeroExpediente cannot be null or blank"() {
            when:
            domain.numeroExpediente = null
    
            then:
            !domain.validate(['numeroExpediente'])
            domain.errors['numeroExpediente'].code == 'nullable'
    
            when:
            domain.numeroExpediente = ''
    
            then:
            !domain.validate(['numeroExpediente'])
            domain.errors['numeroExpediente'].code == 'blank'
    
            when: "numeroExpediente is 21 characters long"
            domain.numeroExpediente = 'a' * 21
    
            then: "numeroExpediente validation fails"
            !domain.validate(['numeroExpediente'])
            domain.errors['numeroExpediente'].code == 'maxSize.exceeded'
    
            when: "numeroExpediente is 20 characters long"
            domain.numeroExpediente = 'a' * 20
    
            then: "numeroExpediente validation succeed"
            domain.validate(['numeroExpediente'])
        }
    
        // More tests
    }