9.2 Integration Testing - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: null
9.2 Integration Testing
Integration tests differ from unit tests in that you have full access to the Grails environment within the test. Grails uses an in-memory H2 database for integration tests and clears out all the data from the database between tests.One thing to bear in mind is that logging is enabled for your application classes, but it is different from logging in tests. So if you have something like this:class MyServiceTests extends GroovyTestCase { void testSomething() { log.info "Starting tests" … } }
log property in the example above is an instance of java.util.logging.Logger (inherited from the base class, not injected by Grails), which doesn't have the same methods as the log property injected into your application artifacts. For example, it doesn't have debug() or trace() methods, and the equivalent of warn() is in fact warning().Transactions
Integration tests run inside a database transaction by default, which is rolled back at the end of the each test. This means that data saved during a test is not persisted to the database. Add atransactional property to your test class to check transactional behaviour:class MyServiceTests extends GroovyTestCase { static transactional = false void testMyTransactionalServiceMethod() { … } }
tearDown method, so these tests don't interfere with standard transactional tests that expect a clean database.Testing Controllers
To test controllers you first have to understand the Spring Mock Library.Grails automatically configures each test with a MockHttpServletRequest, MockHttpServletResponse, and MockHttpSession that you can use in your tests. For example consider the following controller:class FooController { def text() {
render "bar"
} def someRedirect() {
redirect(action:"bar")
}
}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 } }
response is an instance of MockHttpServletResponse which we can use to obtain the generated content with contentAsString (when writing to the response) or the redirected URL. These mocked versions of the Servlet API are completely mutable (unlike the real versions) and hence you can set properties on the request such as the contextPath and so on.Grails does not invoke interceptors or servlet filters when calling actions during integration testing. You should test interceptors and filters in isolation, using 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
}
}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) {
…
}
}def controller = new AuthenticationController() controller.params.login = "marcpalmer" controller.params.password = "secret" controller.params.passwordConfirm = "secret" controller.signup()
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 lets you 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])
}
}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.bookSimulating Request Data
You can use the Spring MockHttpServletRequest to test an action that requires request data, for example a REST web service. For example consider this action which performs data binding from an incoming request:def create() {
[book: new Book(params.book)]
}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>
'''.stripIndent().getBytes() // note we need the bytes def model = controller.create()
assert model.book
assertEquals "The Stand", model.book.title
}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 theFor more information on the subject of REST web services see the section on REST.classproperty to specify the name the target type to bind to. In XML this is implicit within the name of the<book>node, but this property is required as part of the JSON packet.
Testing Web Flows
Testing Web Flows requires a special test harness calledgrails.test.WebFlowTestCase which subclasses Spring Web Flow's AbstractFlowExecutionTests class.
Subclasses of WebFlowTestCase must be integration tests
For example given this simple 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")
}
…
}
}getFlow
method:import grails.test.WebFlowTestCaseclass ExampleFlowTests extends WebFlowTestCase { def getFlow() { new ExampleController().exampleFlow } … }
getFlowId method, otherwise the default is test:
import grails.test.WebFlowTestCaseclass ExampleFlowTests extends WebFlowTestCase { String getFlowId() { "example" } … }
protected void setUp() { super.setUp() registerFlow("other/otherSub") { // register a simplified mock start { on("next").to("end") } end() } // register the original subflow registerFlow("example/sub", new ExampleController().subFlow) }
startFlow method:void testExampleFlow() {
def viewSelection = startFlow()
…
}signalEvent method to trigger an event:void testExampleFlow() {
…
signalEvent("go")
assert "next" == flowExecution.activeSession.state.id
assert "world" == flowScope.hello
}hello variable into the flow scope.Testing Tag Libraries
Testing tag libraries is simple because when a tag is invoked as a method it returns its result as a string (technically aStreamCharBuffer but this class implements all of the methods of 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}>"
}
}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() } }
testBodyTag, we pass a block that returns the body of the tag. This is convenient to representing the body as a String.Testing Tag Libraries with GroovyPagesTestCase
In addition to doing simple testing of tag libraries like in the above examples, you can also use thegrails.test.GroovyPagesTestCase class to test tag libraries with integration tests.The GroovyPagesTestCase class is a subclass of the standard GroovyTestCase class and adds utility methods for testing the output of GSP rendering.
GroovyPagesTestCase can only be used in an integration test.
For example, consider this date formatting tag library:import java.text.SimpleDateFormatclass FormatTagLib { def dateFormat = { attrs, body -> out << new SimpleDateFormat(attrs.format) << attrs.date } }
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]) } }
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, but there are a few things to be aware of. Firstly, when testing queries you often need to "flush" 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()
}Book instances when called. Calling save only indicates to Hibernate that at some point in the future these instances should be persisted. To commit changes immediately you "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()
}flush with a value of true the updates will be persisted immediately and hence will be available to the query later on.

