Search code examples
regexunit-testinggrailsgrails-ormgrails-domain-class

Grails Domain Class Unit Test, issue with Constraint setup - java.lang.NoClassDefFoundError: Could not initialize


My grails unit test for domain class is throwing this exception:

|  java.lang.NoClassDefFoundError: Could not initialize class com.pkg.common.Configuration
    at com.pkg.DomainClass$__clinit__closure1.doCall(DomainClass.groovy:10)
    at org.grails.datastore.mapping.config.groovy.MappingConfigurationBuilder.evaluate(MappingConfigurationBuilder.groovy:72)
    at org.grails.datastore.mapping.config.AbstractGormMappingFactory.createMappedForm(AbstractGormMappingFactory.java:51)
    at org.grails.datastore.mapping.keyvalue.mapping.config.GormKeyValueMappingFactory.createMappedForm(GormKeyValueMappingFactory.java:37)
    at org.grails.datastore.mapping.keyvalue.mapping.config.GormKeyValueMappingFactory.createMappedForm(GormKeyValueMappingFactory.java:27)
    at org.grails.datastore.mapping.keyvalue.mapping.config.KeyValuePersistentEntity.<init>(KeyValuePersistentEntity.java:33)
    at org.grails.datastore.mapping.keyvalue.mapping.config.KeyValueMappingContext.createPersistentEntity(KeyValueMappingContext.java:89)
    at org.grails.datastore.mapping.model.AbstractMappingContext.addPersistentEntityInternal(AbstractMappingContext.java:159)
    at org.grails.datastore.mapping.model.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:152)
    at grails.test.mixin.domain.DomainClassUnitTestMixin.mockDomain(DomainClassUnitTestMixin.groovy:133)
    at grails.test.mixin.domain.DomainClassUnitTestMixin.mockDomain(DomainClassUnitTestMixin.groovy:129)

Following is my stripped down Domain class :

package com.pkg

import com.pkg.common.Configuration

class DomainClass {

    String subject

    static constraints = {
        subject(maxSize: 100, nullable: false, blank: false, matches: Configuration.ALLOWED_TEXT_CHARS)
    }
}

My com.pkg.common.Configuration class is just a Singleton with static properties :

package com.pkg.common

import grails.util.Holders

class Configuration{
    final public static String ALLOWED_TEXT_CHARS = "${Holders.grailsApplication.metadata["allowed.text.chars"]}"
}

"allowed.text.chars" is a RegEx defined in application.properties :

allowed.text.chars=[a-zA-Z0-9\\p{L}\\p{Digit}_"` $ \\-'_&;\:#()/\\.,!\\?%*|<>]+

Now, why this much pain just to set up a constraint ? Well, I need to keep all text fields across my 15+ domains, consistent in terms of characters they accept. Since this is business driven requirement, I need to keep it configurable, so that we can remove/add any new chars at any time, without the risk of touching too many domain classes for the sake of integrity. In addition to that, I want to use the available regex in other Service classes as well.

Now, this implementation works well in run-app, production & test-app integration: Only problem is with Unit tests cases. What I can understand is, that such dependencies, Grails won't be able to inject automatically, in a unit-test env.

Can we mock & add Configuration class using metaClass manipulation ? I have tried but failed.(read: I am not aware how mocking for Static classes work and how to add them to metaClass)

Or, if there's another way to implement such kind of concept ?

Many Thnx


Solution

  • To avoid repeating the constraint across multiple domain classes, you could use either a shared constraint or a global constraint.

    If you use a global constraint, then to enable you to access the regex from a service class, store the regex in a separate config param.

    Config.groovy

    allowed.text.chars='bigLongRegex'
    
    grails.gorm.default.constraints = {
        subject matches: allowed.text.chars
    }
    

    Service Class

    class MyService {
    
       GrailsApplication grailsApplication
    
       void something() {
          String regex = grailsApplication.config.allowed.text.chars
       }
    }
    

    If you want to write a unit test that tests the domain class' constraint remember to add the @Mock annotation, e.g.

    @Mock([DomainClass])
    class DomainClassTests {
       void testConstraint() {
         // test code goes here
       }
    }