Search code examples
grailsgradlespring-bootgrails-ormspock

Spring boot, GORM, and unit tests


I have a project built using Spring Boot. In this project I have included the gorm-hibernate4-spring-boot plugin in order to use Grails' GORM object mapping sugar. When running the project, GORM does its thing without issue and everything is great. Tests, on the other hand, are another matter. In a Grails project I would need to annotate my test cases with @Mock([DomainOne, DomainTwo]). The GORM plugin for spring boot works differently.

An answer for another question around Spring Boot and GORM linked to HibernateMixin from the grails-datastore-test-support plugin. In order to use this mixin, a project must also have the grails @TestMixin annotation which is included in an artifact that pulls in the rest of Grails. This same answer also suggested use of HibernateDatastoreSpringInitializer in order to initialize GORM in a spec's setup() method. Unfortunately I could not get this to work despite seeming to be the best bet.

Here's my build.gradle:

buildscript { // Defines dependencies and repos for the build script itself
  repositories { mavenCentral() }
  dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.7.RELEASE") }
}

apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'spring-boot'

version = 1.0
mainClassName='foo.bar.Baz'

repositories {
  mavenCentral()
  maven { url 'http://repo.spring.io/libs-milestone' }
  maven { url 'http://repo.spring.io/libs-snapshots' }
  flatDir { dirs 'lib' }
}

dependencies {
  compile("org.springframework.boot:spring-boot-starter-jdbc") {
      exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
  }
  compile("org.apache.activemq:activemq-broker") // ActiveMQ
  compile('commons-io:commons-io:2.4') // Commons IO
  compile("org.springframework.boot:spring-boot-starter-log4j") // log 4j
  compile('org.codehaus.groovy:groovy-all')
  compile('org.postgresql:postgresql:9.4-1201-jdbc41') // JDBC
  compile('log4j:log4j:1.2.17') // Default logging provider
  compile("org.grails:gorm-hibernate4-spring-boot:5.0.0.RC2") // I summon thee, gorm
  testCompile("org.spockframework:spock-core:0.7-groovy-2.0")
  testCompile("org.springframework.boot:spring-boot-starter-test")
  testCompile('com.h2database:h2:1.3.148') // h2 for GORM use during tests
}

task wrapper(type: Wrapper) {
  gradleVersion = '2.3'
}

Here's my test case:

class DataTypeValidatorSpec extends Specification {

    def setup() {
        // Fails with "org.hibernate.HibernateException: No Session found for current thread"
        def datastoreInitializer = new HibernateDatastoreSpringInitializer(MyDomainClass)
        def applicationContext = new GenericApplicationContext()
        def dataSource = new DriverManagerDataSource(
            "jdbc:h2:mem:grailsDb1;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_DELAY=-1",
            'sa', ''
        )
        dataSource.driverClassName = Driver.name
        applicationContext.beanFactory.registerSingleton("dataSource", dataSource)
        datastoreInitializer.configureForBeanDefinitionRegistry(applicationContext)
        applicationContext.refresh() // is this needed?
    }

    void "should do gorm stuff"() {
        given:
            MyDomainClass instance = new MyDomainClass(name: 'foo').save()
        when:
            MyDomainClass locatedInstance = MyDomainClass.findByName('foo')
        then:
            instance == locatedInstance
    }

}

I run the tests with gradle test. The code in the setup method for my spec is a pretty direct translation of the setup used in the HibernateDatastoreSpringInitializerSpec linked above.

Note, this is not a Grails project and if possible I'd like to keep the Grails related code to a minimum. Thanks in advance!


Solution

  • I was not able to get mocking to work without dragging in Grails core. I just removed GORM and went with Spring JPA / Hibernate based entities and repositories. These are easy to use with Spock and provide a more visible code flow for persisting, updating, etc. Sorry GORM.