grails / grails-data-mapping

GORM - Groovy Object Mapping
http://gorm.grails.org/
218 stars 198 forks source link

Properties of type java.time.Year are not recognized in tests #1027

Open piotrgajow opened 6 years ago

piotrgajow commented 6 years ago

Using dynamic finders for properties of java.time.Year type in test code results in InvalidDataAccessResourceUsageException - Cannot query (...) on non-existent property: (...), while in regular code it works fine.

Debugging had shown that java.time.Year is not recognized as a simple type in MappingFactory.

Task List

Steps to Reproduce

  1. Create domain class with property of type java.time.Year (e.g. year)
  2. In unit test use dynamic finder with the java.time.Year property (findByYear())

Expected Behaviour

Dynamic finder should work as in non-test code.

Actual Behaviour

Using dynamic finder throws exception:

org.springframework.dao.InvalidDataAccessResourceUsageException: Cannot query [sandbox.TestDomain] on non-existent property: year
    at org.grails.datastore.mapping.simple.query.SimpleMapQuery.getValidProperty(SimpleMapQuery.groovy:751)
    at org.grails.datastore.mapping.simple.query.SimpleMapQuery.executeSubQueryInternal(SimpleMapQuery.groovy:690)
    at org.grails.datastore.mapping.simple.query.SimpleMapQuery.executeSubQuery(SimpleMapQuery.groovy:676)
    at org.grails.datastore.mapping.simple.query.SimpleMapQuery.executeQuery(SimpleMapQuery.groovy:63)
    at org.grails.datastore.mapping.query.Query.doList(Query.java:575)
    at org.grails.datastore.mapping.query.Query.singleResult(Query.java:563)
    at org.grails.datastore.gorm.finders.AbstractFindByFinder.invokeQuery(AbstractFindByFinder.java:35)
    at org.grails.datastore.gorm.finders.AbstractFindByFinder$1.doInSession(AbstractFindByFinder.java:29)
    at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:319)
    at org.grails.datastore.gorm.finders.AbstractFinder.execute(AbstractFinder.java:42)
    at org.grails.datastore.gorm.finders.AbstractFindByFinder.doInvokeInternal(AbstractFindByFinder.java:27)
    at org.grails.datastore.gorm.finders.DynamicFinder.invoke(DynamicFinder.java:174)
    at org.grails.datastore.gorm.finders.DynamicFinder.invoke(DynamicFinder.java:374)
    at org.grails.datastore.gorm.GormStaticApi.methodMissing(GormStaticApi.groovy:173)
    at org.grails.datastore.gorm.GormEntity$Trait$Helper.staticMethodMissing(GormEntity.groovy:749)
    at sandbox.TestDomainSpec.Dynamic finder should work with java.time.Year(TestDomainSpec.groovy:16)

Environment Information

Example Application

GitHub repository: https://github.com/piotrgajow/Sandbox Branch: grails-JavaTimeYearPropertyIssue - https://github.com/piotrgajow/Sandbox/tree/grails-JavaTimeYearPropertyIssue

Notable files:

Domain class. Finder working properly in Bootstrap. Finder not working in test code.

jeffscottbrown commented 6 years ago

One workaround is to use hibernate in your test:

package sandbox

import grails.transaction.Rollback
import org.grails.orm.hibernate.HibernateDatastore
import org.springframework.transaction.PlatformTransactionManager
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

import java.time.Year

class TestDomainSpec extends Specification {

    @AutoCleanup
    @Shared
    HibernateDatastore hibernateDatastore

    @Shared
    PlatformTransactionManager transactionManager

    void setupSpec() {
        hibernateDatastore = new HibernateDatastore(TestDomain)
        transactionManager = hibernateDatastore.getTransactionManager()
    }

    @Rollback
    void "Dynamic finder should work with java.time.Year"() {
        given:
        new TestDomain(year: Year.of(2000)).save(flush: true, failOnError: true)

        expect:
        TestDomain.findByYear(Year.of(2000))
    }
}
jeffscottbrown commented 6 years ago

Another way to accomplish basically the same thing:

package sandbox

import grails.test.hibernate.HibernateSpec

import java.time.Year

class TestDomainSpec extends HibernateSpec {

    void "Dynamic finder should work with java.time.Year"() {
        given:
        new TestDomain(year: Year.of(2000)).save(flush: true, failOnError: true)

        expect:
        TestDomain.findByYear(Year.of(2000))
    }

    List<Class> getDomainClasses() {
        [TestDomain]
    }
}
jeffscottbrown commented 6 years ago

Both of those approaches should work with your version of Grails (3.2.8) and GORM (6.0.9.RELEASE).

piotrgajow commented 6 years ago

I have tried the HibernateSpec workaround and it did fix the issue, thank you!