Integration tests differ from unit tests in that you have full access to the Grails environment within the test. Grails will use an in-memory HSQLDB database for integration tests and clear out all the data from the database in between each test.
Transactions
The integration tests run inside a database transaction by default, which is then rolled back at the end of the tests. This means that data saved during the tests is not persisted to the database. If you actually want to check transactional behaviour of your services and controllers, then you can disable a test's transaction by adding a
transactional
property to your test case:
class MyServiceTestCase extends GroovyTestCase {
static transactional = false void testMyTransactionalServiceMethod() {
…
}
}
Testing Controllers
To test controllers you first have to understand the Spring Mock Library.
Essentially Grails automatically configures each test with a
MockHttpServletRequest,
MockHttpServletResponse, and
MockHttpSession which you can then use to perform your tests. For example consider the following controller:
class FooController { def text = {
render "bar"
} def someRedirect = {
redirect(action:"bar")
}
}
The tests for this would be:
class FooControllerTests extends GroovyTestCase { void testText() {
def fc = new FooController()
fc.text()
assertEquals "bar", fc.response.contentAsString
} void testSomeRedirect() { def fc = new FooController()
fc.someRedirect()
assertEquals "/foo/bar", fc.response.redirectedUrl
}
}
In the above case the response is an instance of
MockHttpServletResponse
which we can use to obtain the
contentAsString
(when writing to the response) or the URL redirected to for example. These mocked versions of the Servlet API are, unlike the real versions, all completely mutable and hence you can set properties on the request such as the
contextPath
and so on.
Grails
does not invoke
interceptors or servlet filters automatically when calling actions during integration testing. You should test interceptors and filters in isolation, and via
functional testing if necessary.
Testing Controllers with Services
If your controller references a service (or other Spring beans), you have to explicitly initialise the service from your test.
Given a controller using a service:
class FilmStarsController {
def popularityService def update = {
// do something with popularityService
}
}
The test for this would be:
class FilmStarsTests extends GroovyTestCase {
def popularityService void testInjectedServiceInController () {
def fsc = new FilmStarsController()
fsc.popularityService = popularityService
fsc.update()
}
}
Testing Controller Command Objects
With command objects you just supply parameters to the request and it will automatically do the command object work for you when you call your action with no parameters:
Given a controller using a command object:
class AuthenticationController {
def signup = { SignupForm form ->
…
}
}
You can then test it like this:
def controller = new AuthenticationController()
controller.params.login = "marcpalmer"
controller.params.password = "secret"
controller.params.passwordConfirm = "secret"
controller.signup()
Grails auto-magically sees your call to
signup()
as a call to the action and populates the command object from the mocked request parameters. During controller testing, the
params
are mutable with a mocked request supplied by Grails.
Testing Controllers and the render Method
The
render method allows you to render a custom view at any point within the body of an action. For instance, consider the example below:
def save = {
def book = Book(params)
if(book.save()) {
// handle
}
else {
render(view:"create", model:[book:book])
}
}
In the above example the result of the model of the action is not available as the return value, but instead is stored within the
modelAndView
property of the controller. The
modelAndView
property is an instance of Spring MVC's
ModelAndView class and you can use it to the test the result of an action:
def bookController = new BookController()
bookController.save()
def model = bookController.modelAndView.model.book
Simulating Request Data
If you're testing an action that requires request data such as a REST web service you can use the Spring
MockHttpServletRequest object to do so. For example consider this action which performs data binding from an incoming request:
def create = {
[book: new Book(params['book']) ]
}
If you wish the simulate the 'book' parameter as an XML request you could do something like the following:
void testCreateWithXML() {
def controller = new BookController()
controller.request.contentType = 'text/xml'
controller.request.content = '''<?xml version="1.0" encoding="ISO-8859-1"?>
<book>
<title>The Stand</title>
…
</book>
'''.getBytes() // note we need the bytes def model = controller.create()
assert model.book
assertEquals "The Stand", model.book.title
}
The same can be achieved with a JSON request:
void testCreateWithJSON() {
def controller = new BookController()
controller.request.contentType = "text/json"
controller.request.content = '{"id":1,"class":"Book","title":"The Stand"}'.getBytes() def model = controller.create()
assert model.book
assertEquals "The Stand", model.book.title
}
With JSON don't forget the class
property to specify the name the target type to bind too. In the XML this is implicit within the name of the <book>
node, but with JSON you need this property as part of the JSON packet.
For more information on the subject of REST web services see the section on
REST.
Testing Web Flows
Testing
Web Flows requires a special test harness called
grails.test.WebFlowTestCase
which sub classes Spring Web Flow's
AbstractFlowExecutionTests class.
Subclasses of WebFlowTestCase
must be integration tests
For example given this trivial flow:
class ExampleController {
def exampleFlow = {
start {
on("go") {
flow.hello = "world"
}.to "next"
}
next {
on("back").to "start"
on("go").to "subber"
}
subber {
subflow(action: "sub")
on("end").to("end")
}
end()
} def subFlow = {
subSubflowState {
subflow(controller: "other", action: "otherSub")
on("next").to("next")
}
…
}
}
You need to tell the test harness what to use for the "flow definition". This is done via overriding the abstract
getFlow
method:
class ExampleFlowTests extends grails.test.WebFlowTestCase {
def getFlow() { new ExampleController().exampleFlow }
…
}
If you need to specify the flow id you can do so by overriding the getFlowId method otherwise the default is
test
:
class ExampleFlowTests extends grails.test.WebFlowTestCase {
String getFlowId() { "example" }
…
}
If the flow under test calls any subflows, these (or mocks) need to be registered before the calling flow :
protected void setUp() {
super.setUp()
registerFlow("other/otherSub") { // register a simplified mock
start {
on("next").to("end")
}
end()
}
registerFlow("example/sub", new ExampleController().subFlow) // register the original subflow
}
Once this is done in your test you need to kick off the flow with the
startFlow
method:
void testExampleFlow() {
def viewSelection = startFlow()
…
}
To trigger and event you need to use the
signalEvent
method:
void testExampleFlow() {
…
signalEvent("go")
assert "next" == flowExecution.activeSession.state.id
assert "world" == flowScope.hello
}
Here we have signaled to the flow to execute the event "go" this causes a transition to the "next" state. In the example a transition action placed a
hello
variable into the flow scope.
Testing Tag Libraries
Testing tag libraries is actually pretty trivial because when a tag is invoked as a method it returns its result as a string. So for example if you have a tag library like this:
class FooTagLib {
def bar = { attrs, body ->
out << "<p>Hello World!</p>"
} def bodyTag = { attrs, body ->
out << "<${attrs.name}>"
out << body()
out << "</${attrs.name}>"
}
}
The tests would look like:
class FooTagLibTests extends GroovyTestCase { void testBarTag() {
assertEquals "<p>Hello World!</p>", new FooTagLib().bar(null,null).toString()
} void testBodyTag() {
assertEquals "<p>Hello World!</p>", new FooTagLib().bodyTag(name:"p") {
"Hello World!"
}.toString()
}
}
Notice that for the second example,
testBodyTag
, we pass a block that returns the body of the tag. This is handy for representing the body as a String.
Testing Tag Libraries with GroovyPagesTestCase
In addition to doing simply testing of tag libraries like the above you can also use the
grails.test.GroovyPagesTestCase
class to test tag libraries.
The
GroovyPagesTestCase
class is a sub class of the regular
GroovyTestCase
class and provides utility methods for testing the output of a GSP rendering.
GroovyPagesTestCase
can only be used in an integration test.
As an example given a date formatting tag library such as the one below:
class FormatTagLib {
def dateFormat = { attrs, body ->
out << new java.text.SimpleDateFormat(attrs.format) << attrs.date
}
}
This can be easily tested as follows:
class FormatTagLibTests extends GroovyPagesTestCase {
void testDateFormat() {
def template = '<g:dateFormat format="dd-MM-yyyy" date="${myDate}" />' def testDate = … // create the date
assertOutputEquals( '01-01-2008', template, [myDate:testDate] )
}
}
You can also obtain the result of a GSP using the
applyTemplate
method of the
GroovyPagesTestCase
class:
class FormatTagLibTests extends GroovyPagesTestCase {
void testDateFormat() {
def template = '<g:dateFormat format="dd-MM-yyyy" date="${myDate}" />' def testDate = … // create the date
def result = applyTemplate( template, [myDate:testDate] ) assertEquals '01-01-2008', result
}
}
Testing Domain Classes
Testing domain classes is typically a simple matter of using the
GORM API, however there are some things to be aware of. Firstly, if you are testing queries you will often need to "flush" in order to ensure the correct state has been persisted to the database. For example take the following example:
void testQuery() {
def books = [ new Book(title:"The Stand"), new Book(title:"The Shining")]
books*.save() assertEquals 2, Book.list().size()
}
This test will actually fail, because calling
save does not actually persist the
Book
instances when called. Calling
save
merely indicates to Hibernate that at some point in the future these instances should be persisted. If you wish to commit changes immediately you need to "flush" them:
void testQuery() {
def books = [ new Book(title:"The Stand"), new Book(title:"The Shining")]
books*.save(flush:true) assertEquals 2, Book.list().size()
}
In this case since we're passing the argument
flush
with a value of
true
the updates will be persisted immediately and hence will be available to the query later on.