(Quick Reference)

14 GORM and Testing

Version: 2023.3.0-SNAPSHOT

14 GORM and Testing

In previous versions of GORM it was much more difficult to setup a unit test to test your GORM logic.

However, since GORM 6.0, this situation has changed and it is relatively trivial to setup GORM for testing.

14.1 Unit Testing with Spock

Spock is the recommended tool for writing unit tests with GORM and is trivial to setup.

GORM with Hibernate and Spock Basics

The following is an example Spock unit test:

import spock.lang.*
import grails.gorm.annotation.Entity
import org.grails.orm.hibernate.HibernateDatastore

class ExampleSpec extends Specification { (1)

    @Shared @AutoCleanup HibernateDatastore hibernateDatastore (2)

    void setupSpec() {
       hibernateDatastore = new HibernateDatastore(Person) (3)
    }

    void "test something"() { (4)
       // your logic here
    }
}

@Entity (5)
class Person {
    ...
}
1 The test should extend spock.lang.Specification
2 The Shared annotation is used to indicate to Spock that the HibernateDatastore is shared across all tests. The AutoCleanup annotation makes sure that HibernateDatastore is shutdown when all tests finish executing.
3 Within the setupSpec method a new HibernateDatastore is constructed with the classes to use as the argument to the constructor.
4 You then write your test logic within each method
5 You can inline domain classes within the unit test if you annotate them with @Entity

Spock and Transactions

Note that in general you have to wrap your test execution logic in a session or transaction. The easiest way to do this is with grails.gorm.transactions.Transactional:

...
import grails.gorm.transactions.*
import org.springframework.transaction.PlatformTransactionManager

class ExampleSpec extends Specification {

    @Shared @AutoCleanup HibernateDatastore hibernateDatastore
    @Shared PlatformTransactionManager transactionManager (1)

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

    @Transactional (3)
    def setup() {
        new Person(firstName:"Fred").save()
    }

    @Rollback (4)
    void "test execute GORM standalone in a unit test"() {
        // your logic here
    }
}
1 The PlatformTransactionManager is defined as a Shared field
2 You can obtain the PlatformTransactionManager from the HibernateDatastore
3 The Transactional annotation is used to setup test data
4 The Rollback annotation is used to rollback any changes made within each test

In the example above, each test method is wrapped in a transaction that rolls back any changes using the grails.gorm.transactions.Rollback annotation.

If you want to setup some test data within the setupSpec method that is shared across all tests then you can use withTransaction:
...
void setupSpec() {
    hibernateDatastore = new HibernateDatastore(Person)
    ...
    Person.withTransaction {
        new Person(firstName:"Fred").save()
    }
}

Configuring GORM in Spock

If you need to configure GORM within a Spock unit test you can pass a map to the constructor of HibernateDatastore. For example to setup multi-tenancy:

...
void setupSpec() {
    Map configuration = [
        'grails.gorm.multiTenancy.mode':'DISCRIMINATOR',
        'grails.gorm.multiTenancy.tenantResolverClass':SystemPropertyTenantResolver
    ]
    hibernateDatastore = new HibernateDatastore(configuration, Person)
    ...
}

14.2 Unit Testing with JUnit

To unit test with JUnit it is largely similar to Spock, just following different idioms.

So instead of setupSpec use @BeforeClass:

import org.junit.*
import grails.gorm.transactions.*
import org.grails.orm.hibernate.HibernateDatastore
import org.springframework.transaction.PlatformTransactionManager

class ExampleTest  {

    static HibernateDatastore hibernateDatastore

    PlatformTransactionManager transactionManager

    @BeforeClass
    void setupGorm() {
       hibernateDatastore = new HibernateDatastore(Person)
    }

    @AfterClass
    void shutdownGorm() {
       hibernateDatastore.close()
    }

    @Before
    void setup() {
        transactionManager = hibernateDatastore.getTransactionManager()
    }

    @Rollback
    @Test
    void testSomething() {
       // your logic here
    }
}
JUnit doesn’t have anything like Spock’s AutoCleanup so you must call close() on the HibernateDatastore manually!