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!
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.
This is a Spring Boot application so use the Boot integration testing annotations:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
If you use these GORM will be loaded using the mechanism provided by gorm-hibernate4-spring-boot
Related
Help!
Our plugin project integration tests should hit the database specified in the datasource.groovy, but for some reason they ignore it, and do it in memory.
Its a plugin which provides the core services (i.e. DB access) to several grails apps which are each a grails application.
Datasource.groovy looks like this:
dataSource {
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
dialect = "org.hibernate.dialect.MySQL5InnoDBDialect"
}
environments {
development {
dataSource {
dbCreate = "create-drop"
url = "jdbc:mysql://127.0.0.1:3306/db"
username = "someuser"
password = "somepass"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:mysql://127.0.0.1:3306/db"
username = "someuser"
password = "somepass"
}
}
production {
dataSource {
}
}
}
The test (SiteIntegrationSpec.goovy)
import grails.test.mixin.TestFor
import grails.test.spock.IntegrationSpec
#TestFor(Site)
class SiteIntegrationSpec extends IntegrationSpec {
static transactional = false
def setup() {
}
def cleanup() {
}
void "test something"() {
when:
Site site
site = new Site(name: "asdf", description: "asdfsd").save(failOnError: true)
then:
site.id == 3
when:
Site site2 = Site.get(1L)
then:
site2.name == "root"
}
}
Data already existing in the site table:
ID name description
1 root root
2 test test
The first test should insert a record which will happen to have an ID of 3. It actually inserts with an ID of 1, i.e. its not seeing or hitting the test database, its using some mock or internal db which is not defined anywhere.
The second test fails as instead of retrieving "root" it retrieves "asdf"
What I have tried:
creating a separate DB for test. Didn't help.
specifying -Dgrails.env=test when running tests. Didn't help
running the tests with the DB down. This correctly fails with cant create pool type exception.
changing the test datasource password to an incorrect one - this correctly throws an exception.
grails -Dgrails.env=test test-app com.me.myproject.SiteIntegrationSpec --stacktrace --verbose
So grails is connecting to the test datasource, but then the integration tests are not using it!
Any ideas?
Edit: Site is a domain object:
class Site {
String name
String description
}
Plugin DataSource.groovy files aren't included in the plugin zip, and if you somehow manually or programmatically include them, they'll be ignored. The same goes for Config.groovy, UrlMappings.groovy, and BootStrap.groovy. In general when something is usable from a plugin, if the application has a file with the same name and location, it overrides the plugin's file, so that would keep this from working also.
You could define a dataSource bean in your plugin's doWithSpring that replaces the one Grails creates based on DataSource.groovy that uses values from a file that exists in the plugin zip, or that is located in the application if that makes sense. Note that there are really 3 DataSource beans and two of them are proxies of the "real" one, so you need to define yours as dataSourceUnproxied so the other two proxy yours and retain the behavior that they add.
Another thing that you will need to fix once you resolve this is your use of unit test annotations in an integration test. Never use Mock, TestFor, or any unit test mixin annotation or base class, since their purpose is to establish a fairly realistic environment that makes up for Spring, Hibernate, installed plugins, and lots of Grails functionality not being available, but in an integration test they are available, and the unit test stuff will stomp on the real instances.
Also - why are you using static transactional = false? This disables an important integration test feature where all of your test methods run in a transaction that is rolled back at the end of the tests pass or fail. This ensures that nothing you do in a test influences other tests - everything is independent. If you disable this, you need to undo all of the changes, and it's easy to miss some and introduce false negatives or worse - false positives - into your tests.
I created a Grails plugin (grails create-plugin myplugin1) and noticed that there was no myapp1/grails-app/conf/BootStrap.groovy created like you normally get when you create a Grails app.
I created one like so:
class BootStrap {
def init = {
println("Hello! The plugin is bootstrapping...")
}
def destroy = {
}
}
I then include the plugin with a Grails app (by adding myplugin1 as a plugin inside the app's BuildConfig.groovy). When I issue a grails run-app, I don't see the above println ever executing.
Do Grails plugins not use BootStrap.groovy? If so, where should I put "bootstrap" code that needs to be executed when a plugin is loaded? Otherwise, if I was correct to do this above, why might I not be seeing the "Hello! The plugin is bootstrapping..." message print out?
As always, start with the very well written and maintained documentation.
Plugins do not include Bootstrap.groovy. The following are excluded from plugins (taken from documentation).
grails-app/conf/BootStrap.groovy
grails-app/conf/BuildConfig.groovy (although it is used to generate dependencies.groovy)
grails-app/conf/Config.groovy
grails-app/conf/DataSource.groovy (and any other *DataSource.groovy)
grails-app/conf/UrlMappings.groovy
grails-app/conf/spring/resources.groovy
Everything within /web-app/WEB-INF
Everything within /web-app/plugins/**
Everything within /test/**
SCM management files within /.svn/ and /CVS/
In order to run code on startup of your plugin you need to hook into the runtime configuration of your plugin by using the doWithSpring or doWithApplicationContext (depending on what you need to do).
All of this, and more, is explained in the documentation. An example might be:
// MyFancyPlugin.groovy
...
def doWithApplicationContext = { appCtx ->
def sessionFactory = appCtx.sessionFactory
// do something here with session factory
}
...
Plugin's BootStrap is EXCLUDED from the plugin package. You have to do your init-phase in the plugin descriptor, in one or several of the following closures:
def doWithSpring = {
def appName = application.metadata.'app.name'
}
def doWithDynamicMethods = { ctx ->
// TODO Implement registering dynamic methods to classes (optional)
}
def doWithApplicationContext = { applicationContext ->
// TODO Implement post initialization spring config (optional)
}
Code that needs to run at startup time should go in the doWithApplicationContext closure in the plugin descriptor (MyPlugin1GrailsPlugin.groovy).
Alternatively, call it something else (e.g. MyPluginBootStrap.groovy), as it's only the specific classes BootStrap and UrlMappings that are excluded when a plugin is packaged, but any class whose name ends BootStrap is considered a bootstrap artefact.
I have a service method that locks a database row.
public String getNextPath() {
PathSeed.withTransaction { txn ->
def seed = PathSeed.lock(1)
def seedValue = seed.seed
seed.seed++
seed.save()
}
}
This is how my spock test looks like:
void "getNextPath should return a String"() {
when:
def path = pathGeneratorService.getNextPath()
then:
path instanceof String
}
It's just a simple initial test. However I get this error when I run the test:
java.lang.UnsupportedOperationException: Datastore [org.grails.datastore.mapping.simple.SimpleMapSession] does not support locking.
at org.grails.datastore.mapping.core.AbstractSession.lock(AbstractSession.java:603)
at org.grails.datastore.gorm.GormStaticApi.lock_closure14(GormStaticApi.groovy:343)
at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:302)
at org.grails.datastore.gorm.AbstractDatastoreApi.execute(AbstractDatastoreApi.groovy:37)
at org.grails.datastore.gorm.GormStaticApi.lock(GormStaticApi.groovy:342)
at com.synacy.PathGeneratorService.getNextPath_closure1(PathGeneratorService.groovy:10)
at org.grails.datastore.gorm.GormStaticApi.withTransaction(GormStaticApi.groovy:712)
at com.synacy.PathGeneratorService$$EOapl2Cm.getNextPath(PathGeneratorService.groovy:9)
at com.synacy.PathGeneratorServiceSpec.getNextPath should return a String(PathGeneratorServiceSpec.groovy:17)
Does anyone have any idea what this is?
The simple GORM implementation for Unit tests does not support some features, such as locking. Moving your test to an integration test will use the full implementation of GORM instead of the simple implementation used by unit tests.
Typically when you find yourself using anything more than the very basic features of GORM you will need to use integration tests.
Updated 10/06/2014
In more recent versions of Grails and GORM there is now the HibernateTestMixin which allows you to test/use such features in Unit tests. Further information can be found in the documentation.
As a workaround, I was able to get it working by using Groovy metaprogramming. Applied to your example:
def setup() {
// Current spec does not test the locking feature,
// so for this test have lock call the get method
// instead.
PathSeed.metaClass.static.lock = PathSeed.&get
}
When I try run this script to secure my web services on Grails / CXF client I get
"Cannot invoke method getInInterceptors() on null object" on secureServiceFactory
Does secureServiceFactory need to be set somewhere else?
Any ideas:
Code :
class BootStrap {
def secureServiceFactory
def init = { servletContext ->
Map<String, Object> inProps = [:]
inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
Map<QName, Validator> validatorMap = new HashMap<QName, Validator>();
validatorMap.put(WSSecurityEngine.USERNAME_TOKEN, new UsernameTokenValidator() {
#Override
protected void verifyPlaintextPassword(org.apache.ws.security.message.token.UsernameToken usernameToken, org.apache.ws.security.handler.RequestData data)
throws org.apache.ws.security.WSSecurityException {
if(data.username == "wsuser" && usernameToken.password == "secret") {
println "username and password are correct!"
} else {
println "username and password are NOT correct..."
throw new WSSecurityException("user and/or password mismatch")
}
}
});
inProps.put(WSS4JInInterceptor.VALIDATOR_MAP, validatorMap);
secureServiceFactory.getInInterceptors().add(new WSS4JInInterceptor(inProps))
}
Not sure this is a total answer, but, I receive the same errors and I understand that the cxf plugin is meant to wire up service factories that will match the name of your exposed service. I have verified that out of the box, running the grails-cxf plugin using grails run-app the application works. however, by executing grails war on the project creates a war that when deployed to tc server [vfabric-tc-server-developer-2.9.4.RELEASE] tomcat 7 [tomcat-7.0.47.A.RELEASE], this error occurs.
It is also useful to note that out of the box, as the plugin author has noted in other references [http://www.christianoestreich.com/2012/04/grails-cxf-interceptor-injection/] the generated war won't work unless you change test('org.apache.ws.security:wss4j:1.6.7') to compile('org.apache.ws.security:wss4j:1.6.7') and I note that I was unable to make that work, I had to use compile('org.apache.ws.security:wss4j:1.6.9')
Unfortunately, after surpassing this, I run into a third error when deploying the war that doesn't occur in grails run-app:
22-Aug-2014 11:46:05.062 SEVERE [tomcat-http--1] org.apache.catalina.core.StandardWrapperValve.invoke Allocate exception for servlet CxfServlet
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:641)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1159)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:282)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:273)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:979)
at org.apache.cxf.transport.servlet.CXFServlet.loadBus(CXFServlet.java:75)
I'll continue looking at it, but perhaps this war isn't meant to really deploy, but is more meant just for development of the plugin itself. however, if that is the case, it would still be better to work in TC because then we can leverage the code in our own projects with confidence.
I'm developing my first Grails plugin. It has to access a webservice. The Plugin will obviously need the webservice url. What is the best way to configure this without hardcoding it into the Groovy classes? It would be nice with different config for different environments.
You might want to Keep It Simple(tm). You may define the URL directly in Config.groovy -including per-environment settings- and access it from your plugin as needed using grailsApplication.config (in most cases) or a ConfigurationHolder.config object (See further details in the manual).
As an added bonus that setting may also be defined in standard Java property files or on other configuration files specified in grails.config.locations.
e.g. in Config.groovy
// This will be the default value...
myPlugin.url=http://somewhe.re/test/endpoint
environments {
production {
// ...except when running in production mode
myPlugin.url=http://somewhe.re/for-real/endpoint
}
}
later, in a service provided by your plugin
import org.codehaus.groovy.grails.commons.ConfigurationHolder
class MyPluginService {
def url = ConfigurationHolder.config.myPlugin.url
// ...
}
If its only a small (read: one item) config option, it might just be easier to slurp in a properties file. If there are some number of configuration options, and some of them should be dynamic, i would suggest doing what the Acegi Security plugin does - add a file to /grails-app/conf/plugin_name_config.groovy perhaps.
added bonus is that the user can execute groovy code to compute their configuration options (much better over using properties files), as well as being able to do different environments with ease.
check out http://groovy.codehaus.org/ConfigSlurper , which is what grails internally use to slurp configs like config.groovy.
//e.g. in /grails-app/conf/MyWebServicePluginConfig.groovy
somePluginName {
production {
property1 = "some string"
}
test {
property1 = "another"
}
}
//in your myWebServicePlugin.groovy file, perhaps in the doWithSpring closure
GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader())
ConfigObject config
try {
config = new ConfigSlurper().parse(classLoader.loadClass('MyWebServicePluginConfig'))
} catch (Exception e) {/*??handle or what? use default here?*/}
assert config.test.property1.equals("another") == true