The problem
Well all would be really easy if not for the fact that I wanted to do an end-to-end test just like a client would use the generated API for a domain class. In this case using integration tests was out of the question because the actual http listener is not running. And I wanted to test it all the way through...
After some digging I've found out that there's a number of plugins that provide this kind of functionality where the application starts and tests are being executed. Those plugins are functional-test, easyb-acceptance-test and webtest.
The unhappy seeker
I've tried to play around with those and one thing struck me down the most: they are mostly targeted at testing web pages and not RESTful services. So they were not as nice for my case as I'd like them to be.
What made me wonder though is that all of those plugins do exactly the same thing to actually spawn the application before tests:
eventAllTestsStart = {
if (getBinding().variables.containsKey("functionalTests")) {
functionalTests << "functional"
}
}
Obviously different plugins use different names for their tests but the idea remains the same. So I asked myself what would happen if I'd introduce the same thing in my scripts/_Events.groovy without those plugins installed... Much to my surprise Grails build system started the application right after spitting out "Starting integration test phase ..." :)The road continues
Now all I had to do was to find out a nice way to actually get the test going. As it turns out there's a fantastic utility called HTTPBuilder that does exactly what I needed! Just a couple of lines in BuildConfig.groovy later...
test('org.codehaus.groovy.modules.http-builder:http-builder:0.5.0') {
excludes "commons-logging", "xml-apis", "groovy"
}
... and testing RESTful services has never been easier!import groovy.util.GroovyTestCase
import org.junit.Test
import groovyx.net.http.*
class RESTfulTests extends GroovyTestCase {
@Test
void willReturnErrorIfSavingNotSuccessfull() {
def client = new RESTClient("http://localhost:8080/example/api/")
def response = client.post(
path: "person",
body: [ data: [ firstName: 'John', lastName: 'Doe' ] ],
requestContentType : ContentType.JSON.toString())
assert response.status == 200
}
}
If you want to see a more sophisticated usage of this approach with more assertions I encourage you to take a look at the basic application in json-rest-api-examples project.
The obvious
One note to add: if you'd like to run those integration tests as part of your continuous integration it'll most probably fail because the port is already in use by Hudson... To overcome this issue set the server.port parameter to some port value that's not in use. In Hudson it looks more/less like this:
Obviously you need to use this port in your tests instead of the default 8080...
Have fun!