Sunday, March 29, 2009

Creating a Grails Plugin for DBUnit

A colleague of mine Eric Weimer introduced me to Grails. I had already used Groovy to a limited degree but not Grails. Eric presented at the Chicago Groovy Users Group and I really wanted to learn more about Grails.

So I bought the book The Definitive Guide to Grails - 2nd Edition which I highly recommend. After getting through nearly all of the book, I really wanted to start working with Grails, so I took the example from the book and extended the Domain model slightly. The area that I wanted to focus on for now was to get a better understanding of GORM, which is the ORM layer that uses Hibernate.

To do this I created a simple Grails application with just Domain classes and I started to create some integration tests that I could use to get a feel for the GORM api.

However, to do this I quickly ended up needing to seed the database with some data so I could execute queries. In my Java bag of tricks, I have used DBUnit and I immediately wanted to leverage this in my Grails work. Normally I would have checked to see if there was already a DBUnit Grails plugin, but I was actually in 'a dead zone' - and did not have internet access. But this was ok - I already had all of the DBUnit code I needed from my Java projects.

What this blog posting is going to show is how I used the information from the Grails book to create my own DBUnit plugin that I can now leverage in my projects. As it turns out, there is already a DBUnit plugin and I will either submit my version or work with the author of that plugin to see about integrating some of my features into his code base. This is my first venture into Grails - so if someone out there sees a better way to tackle this kind of problem please post a comment so we can all benefit.

Step 1 - create a plugin application
grails create-plugin dbunit

This will create what looks like a typical Grails application. The only real difference is a file in the root directory of the project called: DbunitGrailsPlugin.groovy

Step 2 - Update DbunitGrailsPlugin.groovy

Besides the author,title, etc information the only section I changed was the doWithSpring closure. I decided to take the utility class that I already had for my Java development, and create a Spring bean out of it. This Spring bean can then be used in the Grails integration tests. Below is the updated doWithSpring closure.

def doWithSpring = {
dbunitUtil(com.redpointtech.dbunit.DBUnitUtil) { bean ->
bean.scope = "prototype"
}
}


What this says is to create a bean with the name dbunitUtil of the type com.redpointtech.dbunit.DBUnitUtil and set the scope to prototype. Because my DBUnitUtil does keep track of some DBUnit artifacts, I decided to require a new instance to avoid any thread synchronization issues.

Step 3 - Add my Java class DBUnitUtil.java to Grails src
In the root directory of the Grails plugin application, there is a folder called 'src'. In that folder there is another called 'java', and in there I placed the com.redpointtech.dbunit.DBUnitUtil.java class. Since this post is not about the DBUnit part, I am not going to go into the details of that class but it is included in the source distribution at the end of this blog post.

Step 4 - Add DBUnit jars to the lib dir
In the root directory of the Grails plugin application, there is a lib folder and in there I placed the dbunit-2.4.4.jar file.

Step 5 - package the plugin
Because I have not applied for contributor access, I cannot release the plugin so for now I can only package up the plugin and make it available locally. To package the plugin you use the command from the root of the plugin project:

grails package-plugin

This created a file called gails-dbunit-0.1.zip

Thats it - I now have a plugin I can use in any grails application to help support integration testing.

On a side note - Grails supports unit tests and integration tests. Unit tests are supposed to be very lightweight and will not initialize all of the database infrastructure so you have to create integration tests to test the

I created another application that I called gormtest and I used many of the Domain classed from the Album/Song/Artist example from the Grails book.

To use the new plugin you can install a plugin directly from the zip file using the following command:

grails install-plugin [full path to the gails-dbunit-0.1.zip]

Doing this makes a Spring available to my Integration tests. I created an itegration test using the following:

grails create-integration-test com.redpointtech.gtunes.Album

This created a file called AlbumTests.groovy in the test/integration/com/redpointtech/gtunes directory.

An abbreviated version of the AlbumTests.groovy file follows:

1 package com.redpointtech.gtunes
2
3 import grails.test.*
4 import com.redpointtech.dbunit.*
5 import javax.sql.DataSource
6
7 class AlbumTests extends GrailsUnitTestCase {
8 DataSource dataSource
9 DBUnitUtil dbunitUtil
10
11
12 protected void setUp() {
13 super.setUp()
14 dbunitUtil.onSetup(dataSource, "./data/user-testdata.xml, ./data/album-testdata.xml")
15 }
16
17 protected void tearDown() {
18 super.tearDown()
19 dbunitUtil.onTearDown()
20 }
21
22 void testAlbumFind() {
23 def albums = Album.findAllByTitleLike("title1%")
24 assertEquals albums.size, 3
25 albums.each { album ->
26 println "------------------------------------"
27 println "Title: " + album.getTitle()
28 def artist = album.getArtist()
29 println "Artist: " + artist.getName()
30 }
31 }
32}



On lines 8 and 9, we create two instance variables that Grails will automatically inject the beans into - following the naming convention. The first is the dataSource used for the test and the second is the DBUnitUtil bean that is provided by the Plugin we just created.

On line 14, we use the DBUnit plugin bean, to load the database referenced by the dataSource, with the data in the two XML files.

On line 19, we use the dbunitUtil bean again to tear down the data used for the test. I know someone is going to ask about the time it takes to load and unload all of the data for each test and it is a fair concern. My suggestion is to group as many tests in the single test method as is practical, and since I dont think anyone would really want to create dependencies between their tests you probably do want to start with a fresh data load for the next test.

On line 23, we can then start to use the GORM API, against the data that was loaded by the DBUnit plugin.

The last part to take care of is the XML data files. I placed the data files in the grails-app/conf directory in a subdirectory called data

The user-testdata.xml file looks like the following:
<?xml version="1.0" encoding="UTF-8" ?>

<dataset>
<user id="1000" version="1" first_name="user1_fn" last_name="user1_ln" user_name="user1_un" />
<user id="2000" version="1" first_name="user2_fn" last_name="user2_ln" user_name="user2_un" />
<user id="3000" version="1" first_name="user3_fn" last_name="user3_ln" user_name="user3_un" />
<user id="4000" version="1" first_name="user4_fn" last_name="user4_ln" user_name="user4_un" />


</dataset>



That is pretty much all there is to creating a simple DBUnit plugin, that leveraged the Java code I already had to pre-populate a test database with data so I could start to get familiar with the GORM api through the integration tests.

The source code for both the dbunit plugin and the gormtest application can be found here:

DBUnit Grails Plugin

Gormtest Grails App

No comments: