Recently I was stuck with persistence testing. I like to write simple unit tests and run them from my favorite IDE (Intellij IDEA forever!), but there was no luck with GORM Criterias testing. My case is simple: I have Grails Service which using criteria to fetch some objects from the database. Testing with mockDomain() won’t work since you need Hibernate support. Of course you could still write integration tests as described in Grails tutorial, but I just didn’t want to.
First off I looked through google’s top 10 about the subject, but after few hours of trials I was still where I started.
I decided to look on Grails sources and (voilà!) I found HibernateCriteriaBuilderTests. Which has tested exactly what I messed with.
I’m not going to share all things I’ve done until I got finally working abstract test case (based on AbstractGrailsHibernateTests) . You should just derive your test from test case below and use Grails domain objects in your test as usual. Also I’ll provide two classes MockGrailsPluginManager and MockHibernateGrailsPlugin. Both of them were taken as is from Grails test sources.
AbstractPersistenceTestCase
package com.grailsgeek
import com.grailsgeek.MockHibernateGrailsPlugin
import grails.test.GrailsUnitTestCase
import groovy.lang.ExpandoMetaClass
import groovy.lang.ExpandoMetaClassCreationHandle
import groovy.lang.GroovyClassLoader
import groovy.lang.GroovySystem
import org.codehaus.groovy.grails.commons.ApplicationHolder
import org.codehaus.groovy.grails.commons.DefaultGrailsApplication
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.commons.spring.DefaultRuntimeSpringConfiguration
import org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator
import org.codehaus.groovy.grails.plugins.datasource.DataSourceGrailsPlugin
import org.codehaus.groovy.grails.plugins.i18n.I18nGrailsPlugin
import org.codehaus.groovy.grails.support.MockApplicationContext
import org.hibernate.Session
import org.hibernate.SessionFactory
import org.springframework.context.ApplicationContext
import org.springframework.context.support.StaticMessageSource
import org.springframework.orm.hibernate3.SessionFactoryUtils
import org.springframework.orm.hibernate3.SessionHolder
import org.springframework.transaction.support.TransactionSynchronizationManager
import org.codehaus.groovy.grails.plugins.*
abstract class AbstractPersistenceTestCase extends GrailsUnitTestCase {
GroovyClassLoader gcl = new GroovyClassLoader(this.getClass().classLoader)
MockApplicationContext ctx;
ApplicationContext appCtx
SessionFactory sessionFactory
Session session
protected void setUp() {
super.setUp();
ExpandoMetaClass.enableGlobally()
GroovySystem.metaClassRegistry.metaClassCreationHandle = new ExpandoMetaClassCreationHandle();
gcl.parseClass('''
dataSource {
pooled = true
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""
dbCreate = "create-drop" // one of 'create', 'create-drop','update'
url = "jdbc:hsqldb:mem:testDB"
}
hibernate {
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider'
}
''', "DataSource")
ctx = new MockApplicationContext();
def classes = getDomainClasses()
gcl.getLoadedClasses().each {
classes << it
}
def ga = new DefaultGrailsApplication(classes as Class[], gcl)
def mockManager = new MockGrailsPluginManager(ga)
ctx.registerMockBean("manager", mockManager)
PluginManagerHolder.setPluginManager(mockManager)
def dependentPlugins = [
DataSourceGrailsPlugin,
DomainClassGrailsPlugin,
I18nGrailsPlugin,
MockHibernateGrailsPlugin
].collect { new DefaultGrailsPlugin(it, ga)}
dependentPlugins.each { mockManager.registerMockPlugin(it); it.manager = mockManager }
mockManager.doArtefactConfiguration();
ctx.registerMockBean(PluginMetaManager.BEAN_ID, new DefaultPluginMetaManager());
ga.initialise()
ga.setApplicationContext(ctx);
ApplicationHolder.setApplication(ga)
ctx.registerMockBean(GrailsApplication.APPLICATION_ID, ga);
ctx.registerMockBean("messageSource", new StaticMessageSource())
def springConfig = new DefaultRuntimeSpringConfiguration(ctx, gcl)
dependentPlugins*.doWithRuntimeConfiguration(springConfig)
appCtx = springConfig.getApplicationContext()
dependentPlugins*.doWithApplicationContext(appCtx)
mockManager.applicationContext = appCtx
mockManager.doDynamicMethods()
sessionFactory = appCtx.getBean(GrailsRuntimeConfigurator.SESSION_FACTORY_BEAN);
if (!TransactionSynchronizationManager.hasResource(sessionFactory)) {
session = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
}
abstract List getDomainClasses()
protected void tearDown() {
if (TransactionSynchronizationManager.hasResource(this.sessionFactory)) {
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(this.sessionFactory);
org.hibernate.Session s = holder.getSession();
//s.flush();
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
SessionFactoryUtils.releaseSession(s, this.sessionFactory);
}
ApplicationHolder.setApplication(null)
ExpandoMetaClass.disableGlobally()
PluginManagerHolder.setPluginManager(null)
super.tearDown();
}
}
MockGrailsPluginManager
package com.grailsgeek;
import groovy.lang.GroovyClassLoader;
import junit.framework.Assert;
import org.codehaus.groovy.grails.commons.DefaultGrailsApplication;
import org.codehaus.groovy.grails.commons.GrailsApplication;
import org.codehaus.groovy.grails.plugins.exceptions.PluginException;
import org.springframework.core.io.Resource;
import javax.servlet.ServletContext;
import java.io.File;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map
import org.codehaus.groovy.grails.plugins.AbstractGrailsPluginManager
import org.codehaus.groovy.grails.plugins.GrailsPlugin;
/**
* @author Graeme Rocher
* @since 0.4
*
*/
public class MockGrailsPluginManager extends AbstractGrailsPluginManager {
private ServletContext servletContext;
private boolean checkForChangesExpected = false;
public ServletContext getServletContext() {
return servletContext;
}
public MockGrailsPluginManager(GrailsApplication application) {
super(application);
loadPlugins();
}
public MockGrailsPluginManager() {
this(new DefaultGrailsApplication(new Class[0], new GroovyClassLoader()));
}
public GrailsPlugin getGrailsPlugin(String name) {
return this.plugins.get(name);
}
public GrailsPlugin getGrailsPlugin(String name, BigDecimal version) {
return this.plugins.get(name);
}
public boolean hasGrailsPlugin(String name) {
return this.plugins.containsKey(name);
}
public void registerMockPlugin(GrailsPlugin plugin) {
this.plugins.put(plugin.getName(), plugin);
this.pluginList.add(plugin);
}
public void loadPlugins() throws PluginException {
this.initialised = true;
}
public void checkForChanges() {
Assert.assertTrue(this.checkForChangesExpected);
this.checkForChangesExpected = false;
}
public void doWebDescriptor(Resource descriptor, Writer target) {
// do nothing
}
public void doWebDescriptor(File descriptor, Writer target) {
// do nothing
}
public boolean isInitialised() {
return true;
}
public void refreshPlugin(String name) {
GrailsPlugin plugin = plugins.get(name);
if(plugin != null) {
plugin.refresh();
}
}
public Collection getPluginObservers(GrailsPlugin plugin) {
throw new UnsupportedOperationException("The class [MockGrailsPluginManager] doesn't support the method getPluginObservers");
}
public void informObservers(String pluginName, Map event) {
// do nothing
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void expectCheckForChanges() {
Assert.assertFalse(this.checkForChangesExpected);
this.checkForChangesExpected = true;
}
public void verify() {
Assert.assertFalse(this.checkForChangesExpected);
}
}
MockHibernateGrailsPlugin
package com.grailsgeek
import org.codehaus.groovy.grails.plugins.orm.hibernate.HibernatePluginSupport
class MockHibernateGrailsPlugin {
def version = grails.util.GrailsUtil.getGrailsVersion()
def dependsOn = [dataSource: version,
i18n: version,
core: version,
domainClass: version]
def loadAfter = ['controllers']
def doWithSpring = HibernatePluginSupport.doWithSpring
def doWithDynamicMethods = HibernatePluginSupport.doWithDynamicMethods
}