9 Testing - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: 1.3.9
9 Testing
Automated testing is seen as a key part of Grails, implemented using Groovy Tests. Hence, Grails provides many ways to making testing easier from low level unit testing to high level functional tests. This section details the different capabilities that Grails offers in terms of testing.The first thing to be aware of is that all of thecreate-*
commands actually end up creating unit
tests automatically for you. For example say you run the create-controller command as follows:grails create-controller com.yourcompany.yourapp.simple
grails-app/controllers/com/yourcompany/yourapp/SimpleController.groovy
, but also an unit test at test/unit/com/yourcompany/yourapp/SimpleControllerTests.groovy
. What Grails won't do however is populate the logic inside the test! That is left up to you.
As of Grails 1.2.2,the suffix of Test
is also supported for test cases.
Running Tests
Test are run with the test-app command:grails test-app
------------------------------------------------------- Running Unit Tests… Running test FooTests...FAILURE Unit Tests Completed in 464ms … -------------------------------------------------------Tests failed: 0 errors, 1 failures
target/test-reports
directory.You can force a clean before running tests by passing-clean
to thetest-app
command.
Targeting Tests
You can selectively target the test(s) to be run in different ways. To run all tests for a controller namedSimpleController
you would run:grails test-app SimpleController
SimpleController
. Wildcards can be used...grails test-app *Controller
Controller
. Package names can optionally be specified...grails test-app some.org.*Controller
grails test-app some.org.*
grails test-app some.org.**.*
grails test-app SimpleController.testLogin
testLogin
test in the SimpleController
tests. You can specify as many patterns in combination as you like...grails test-app some.org.* SimpleController.testLogin BookController
Targeting Test Types and/or Phases
In addition to targeting certain tests, you can also target test types and/or phases by using thephase:type
syntax.Grails organises tests by phase and by type. A test phase relates to the state of the Grails application during the tests, and the type relates to the testing mechanism.Grails comes with support for 4 test phases (To execute the JUnitunit
,integration
,functional
andother
) and JUnit test types for theunit
andintegration
phases. These test types have the same name as the phase.Testing plugins may provide new test phases or new test types for existing phases. Refer to the plugin documentation.
integration
tests you can run:grails test-app integration:integration
phase
and type
are optional. Their absence acts as a wildcard. The following command will run all test types in the unit
phase:grails test-app unit:
spock
test type to the unit
, integration
and functional
phases. To run all spock tests in all phases you would run the following:grails test-app :spock
functional
phase you would run...grails test-app functional:spock
grails test-app unit:spock integration:spock
Targeting Tests in Types and/or Phases
Test and type/phase targetting can be applied at the same time:grails test-app integration: unit: some.org.**.*
integration
and unit
phases that are in the page some.org
or a subpackage of.
9.1 Unit Testing
Unit testing are tests at the "unit" level. In other words you are testing individual methods or blocks of code without considering for surrounding infrastructure. In Grails you need to be particularity aware of the difference between unit and integration tests because in unit tests Grails does not inject any of the dynamic methods present during integration tests and at runtime.This makes sense if you consider that the methods injected by Grails typically communicate with the database (with GORM) or the underlying Servlet engine (with Controllers). For example say you have service like the following inBookController
:class MyService { def otherService String createSomething() { def stringId = otherService.newIdentifier() def item = new Item(code: stringId, name: "Bangle") item.save() return stringId } int countItems(String name) { def items = Item.findAllByName(name) return items.size() } }
The Testing Framework
The core of the testing plugin is thegrails.test.GrailsUnitTestCase
class. This is a sub-class of GroovyTestCase
geared towards Grails applications and their artifacts. It provides several methods for mocking particular types as well as support for general mocking a la Groovy's MockFor and StubFor classes.Normally you might look at the MyService
example shown previously and the dependency on another service and the use of dynamic domain class methods with a bit of a groan. You can use meta-class programming and the "map as object" idiom, but these can quickly get ugly. How might we write the test with GrailsUnitTestCase ?import grails.test.GrailsUnitTestCaseclass MyServiceTests extends GrailsUnitTestCase { void testCreateSomething() { // Mock the domain class. mockDomain(Item) // Mock the "other" service. String testId = "NH-12347686" def otherControl = mockFor(OtherService) otherControl.demand.newIdentifier(1..1) {-> return testId } // Initialise the service and test the target method. def testService = new MyService() testService.otherService = otherControl.createMock() def retval = testService.createSomething() // Check that the method returns the identifier returned by the // mock "other" service and also that a new Item instance has // been saved. def testInstances = Item.list() assertEquals testId, retval assertEquals 1, testInstances.size() assertTrue testInstances[0] instanceof Item } void testCountItems() { // Mock the domain class, this time providing a list of test // Item instances that can be searched. def testInstances = [ new Item(code: "NH-4273997", name: "Laptop"), new Item(code: "EC-4395734", name: "Lamp"), new Item(code: "TF-4927324", name: "Laptop") ] mockDomain(Item, testInstances) // Initialise the service and test the target method. def testService = new MyService() assertEquals 2, testService.countItems("Laptop") assertEquals 1, testService.countItems("Lamp") assertEquals 0, testService.countItems("Chair") } }
mockDomain()
method, which is one of several provided by GrailsUnitTestCase
:def testInstances = [] mockDomain(Item, testInstances)
Item
class has been mocked, we can safely call the save()
method on instances of it. Invoking the save()
method doesn't really save the instance to any database but it will cache the object in the testing framework so the instance will be visible to certain queries. The following code snippet demonstrates the effect of calling the save()
method.void testSomething() { def testInstances=[] mockDomain(Song, testInstances) assertEquals(0, Song.count()) new Song(name:"Supper's Ready").save() assertEquals(1, Song.count()) }
mockFor
method:def otherControl = mockFor(OtherService)
otherControl.demand.newIdentifier(1..1) {-> return testId }
MockFor
and StubFor
classes that come with Groovy and it can be used to mock any class you want. In fact, the "demand" syntax is identical to that used by Mock/StubFor, so you should feel right at home. Of course you often need to inject a mock instance as a dependency, but that is pretty straight forward with the createMock()
method, which you simply call on the mock control as shown. For those familiar with EasyMock, the name otherControl
highlights the role of the object returned by mockFor()
- it is a control object rather than the mock itself.The rest of the testCreateSomething()
method should be pretty familiar, particularly as you now know that the mock save()
method adds instances to testInstances
list. However, there is an important technique missing from the test method. We can determine that the mock newIdentifier()
method is called because its return value has a direct impact on the result of the createSomething()
method. But what if that weren't the case? How would we know whether it had been called or not? With Mock/StubFor the check would be performed at the end of the use()
closure, but that's not available here. Instead, you can call verify()
on the control object - in this case otherControl
. This will perform the check and throw an assertion error if it hasn't been called when it should have been.Lastly, testCountItems()
in the example demonstrates another facet of the mockDomain()
method:def testInstances = [ new Item(code: "NH-4273997", name: "Laptop"), new Item(code: "EC-4395734", name: "Lamp"), new Item(code: "TF-4927324", name: "Laptop") ] mockDomain(Item, testInstances)
mockDomain()
method provides a lightweight implementation of the dynamic finders backed by a list of domain instances. Simply provide the test data as the second argument of the method and the mock finders will just work.GrailsUnitTestCase - the mock methods
You have already seen a couple of examples in the introduction of themock..()
methods provided by the GrailsUnitTestCase
class. Here we will look at all the available methods in some detail, starting with the all-purpose mockFor()
. But before we do, there is a very important point to make: using these methods ensures that any changes you make to the given classes do not leak into other tests! This is a common and serious problem when you try to perform the mocking yourself via meta-class programming, but that headache just disappears as long as you use at least one of mock..()
methods on each class you want to mock.mockFor(class, loose = false)
def strictControl = mockFor(MyService) strictControl.demand.someMethod(0..2) { String arg1, int arg2 -> … } strictControl.demand.static.aStaticMethod {-> … }
mockControl.createMock()
. In fact, you can call this as many times as you like to create as many mock instances as you need. And once you have executed the test method, you can call mockControl.verify()
to check whether the expected methods were actually called or not.Lastly, the call:def looseControl = mockFor(MyService, true)
mockDomain(class, testInstances = )
Takes a class and makes mock implementations of all the domain class methods (both instance- and static-level) accessible on it.Mocking domain classes is one of the big wins from using the testing plugin. Manually doing it is fiddly at best, so it's great that mockDomain() takes that burden off your shoulders.In effect,mockDomain()
provides a lightweight version of domain classes in which the "database" is simply a list of domain instances held in memory. All the mocked methods ( save()
, get()
, findBy*()
, etc.) work against that list, generally behaving as you would expect them to. In addition to that, both the mocked save()
and validate() methods will perform real validation (support for the unique constraint included!) and populate an errors object on the corresponding domain instance.There isn't much else to say other than that the plugin does not support the mocking of criteria or HQL queries. If you use either of those, simply mock the corresponding methods manually (for example with mockFor()
) or use an integration test with real data.mockForConstraintsTests(class, testInstances = )
Highly specialised mocking for domain classes and command objects that allows you to check whether the constraints are behaving as you expect them to.Do you test your domain constraints? If not, why not? If your answer is that they don't need testing, think again. Your constraints contain logic and that logic is highly susceptible to bugs - the kind of bugs that can be tricky to track down (particularly as save() doesn't throw an exception when it fails). If your answer is that it's too hard or fiddly, that is no longer an excuse. Enter themockForConstraintsTests()
method.This is like a much reduced version of the mockDomain()
method that simply adds a validate()
method to a given domain class. All you have to do is mock the class, create an instance with field values, and then call validate()
. You can then access the errors property on your domain instance to find out whether the validation failed or not. So if all we are doing is mocking the validate()
method, why the optional list of test instances? That is so that we can test unique constraints as you will soon see.So, suppose we have a simple domain class like so:class Book { String title String author static constraints = { title(blank: false, unique: true) author(blank: false, minSize: 5) } }
class BookTests extends GrailsUnitTestCase { void testConstraints() { def existingBook = new Book(title: "Misery", author: "Stephen King") mockForConstraintsTests(Book, [ existingBook ]) // Validation should fail if both properties are null. def book = new Book() assertFalse book.validate() assertEquals "nullable", book.errors["title"] assertEquals "nullable", book.errors["author"] // So let's demonstrate the unique and minSize constraints. book = new Book(title: "Misery", author: "JK") assertFalse book.validate() assertEquals "unique", book.errors["title"] assertEquals "minSize", book.errors["author"] // Validation should pass! book = new Book(title: "The Shining", author: "Stephen King") assertTrue book.validate() } }
Errors
instance, so you can access all the properties and methods you would normally expect. Second, this particular Errors
object also has map/property access as shown. Simply specify the name of the field you are interested in and the map/property access will return the name of the constraint that was violated. Note that it is the constraint name , not the message code (as you might expect).That's it for testing constraints. One final thing we would like to say is that testing the constraints in this way catches a common error: typos in the "constraints" property! It is currently one of the hardest bugs to track down normally, and yet a unit test for your constraints will highlight the problem straight away.mockLogging(class, enableDebug = false)
Adds a mock "log" property to a class. Any messages passed to the mock logger are echoed to the console.mockController(class)
Adds mock versions of the dynamic controller properties and methods to the given class. This is typically used in conjunction with theControllerUnitTestCase
class.mockTagLib(class)
Adds mock versions of the dynamic taglib properties and methods to the given class. This is typically used in conjunction with theTagLibUnitTestCase
class.
9.2 Integration Testing
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.One thing to bear in mind is that logging is enabled for your application classes, but that 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
, which doesn't have exactly 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
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 atransactional
property to your test case:class MyServiceTests 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") } }
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 } }
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
}
}
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 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]) } }
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']) ]
}
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 }
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.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.
Testing Web Flows
Testing Web Flows requires a special test harness calledgrails.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") } … } }
getFlow
method:class ExampleFlowTests extends grails.test.WebFlowTestCase { def getFlow() { new ExampleController().exampleFlow } … }
test
:
class ExampleFlowTests extends grails.test.WebFlowTestCase { String getFlowId() { "example" } … }
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 }
startFlow
method:void testExampleFlow() { def viewSelection = startFlow() … }
signalEvent
method: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 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}>" } }
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 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 thegrails.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
}
}
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, 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() }
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() }
flush
with a value of true
the updates will be persisted immediately and hence will be available to the query later on.
9.3 Functional Testing
Functional tests involve making HTTP requests against the running application and verifying the resultant behaviour. Grails does not ship with any support for writing functional tests directly, but there are several plugins available for this.Canoo Webtest
- http://grails.org/plugin/webtestG-Func
- http://grails.org/plugin/functional-testSelenium-RC
- http://grails.org/plugin/selenium-rcWebDriver
- http://grails.org/plugin/webdriverGeb
- http://grails.org/plugin/geb
Common Options
There are options that are common to all plugins that control how the Grails application is launched, if at all.inline
The-inline
option specifies that the grails application should be started inline (i.e. like run-app
).This option is implicitly set unless the baseUrl
or war
options are setwar
The-war
option specifies that the grails application should be packaged as a war and started. This is useful as it tests your application in a production like state, but it has a longer startup time than the -inline
option. It also runs the war in a forked JVM, meaning that you cannot access any internal application objects.grails test-app functional: -war
https
The-https
option results in the application being able to receive https requests as well as http requests. It is compatible with both the -inline
and -war
options.grails test-app functional: -https
-httpsBaseUrl
option is also given.httpsBaseUrl
The-httpsBaseUrl
causes the implicit base url to be used for tests to be a https url.grails test-app functional: -httpsBaseUrl
-baseUrl
option is given.baseUrl
ThebaseUrl
option allows the base url for tests to be specified.grails test-app functional: -baseUrl=http://mycompany.com/grailsapp
-inline
or -war
are given as well. If you want to use a custom base url, but still want to test against the local Grails application you must specify one of either the -inline
or -war
options.