9. Testing - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: 1.4.0.M1
Table of Contents
9. Testing
Automated testing is seen as a key part of Grails. 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.
Grails 1.3.x and below used the grails.test.GrailsUnitTestCase class hierarchy for testing in a JUnit 3 style. Grails 1.4.x and above deprecates these test harnesses in favour of mixins that can be applied to a range of different kinds of tests (JUnit 3, Junit 4, Spock etc.)
The first thing to be aware of is that all of the create-* 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.acme.app.simple
grails-app/controllers/com/acme/app/SimpleController.groovy, but also a unit test at test/unit/com/acme/app/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
grails interactive … test-app
test-app command will produce output such as:------------------------------------------------------- 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-cleanto thetest-appcommand.
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,functionalandother) and JUnit test types for theunitandintegrationphases. 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 consideration for surrounding infrastructure. Unit tests are typically run without the presence of physical resources that involve I/O such databases, socket connections or files. This is to ensure they run as quick as possible since quick feedback is important.Since Grails 1.4, a collection of unit testing mixins is provided by Grails that allows you to enhance the behavior of a typical JUnit 3, JUnit 4 or Spock test. The following sections cover the usage of these mixins.
The previous JUnit 3-style GrailsUnitTestCase class hierarchy is still present in Grails for backwards compatibility, but is now considered as deprecated. The previous documentation on the subject can be found in the Grails 1.3.x documentation
9.1.1 Unit Testing Controllers
The Basics
To unit test controllers you use thegrails.test.mixin.TestFor annotation to target a particular controller. Using TestFor in this manner activates the grails.test.mixin.web.ControllerUnitTestMixin and its associated API. For example:import grails.test.mixin.*@TestFor(SimpleController)
class SimpleControllerTests {
void testSomething() { }
}TestFor annotation for a controller a new controller field is automatically created for the controller under test.
The TestFor annotation will also automatically annotate any public methods starting with "test" with JUnit 4's @Test annotation. If your test method doesn't start with "test" you will need to add this manually
To test the simplest "Hello World"-style example you can do the following:// Test class
class SimpleController {
def hello() {
render "hello"
}
}void testHello() {
controller.hello() assert response.text == 'hello'
}response object is an instance of GrailsMockHttpServletResponse (in the package org.codehaus.groovy.grails.plugins.testing) which extends Spring's org.springframework.mock.web.MockHttpServletResponse and has a number of useful methods for inspecting the state of the response.For example to test a redirect you can use the redirectUrl property:// Test class
class SimpleController {
def index() {
redirect action:'hello'
}
…
}void testIndex() {
controller.index() assert response.redirectedUrl == '/simple/hello'
}Testing View Rendering
To test view rendering you can inspect the state of the controller'smodelAndView property which is an instance of org.springframework.web.servlet.ModelAndView or you can use the view and model properties provided by the mixin:// Test class
class SimpleController {
def home() {
render view:"homePage", model:[title:"Hello World"]
}
…
}void testIndex() {
controller.home() assert view == "/simple/homePage"
assert model.title == "Hello World"
}Testing Template Rendering
Unlike view rendering, template rendering will actually attempt to write the template directly to response rather than returning aModelAndView hence it requires a different approach to testing.Consider the following controller action:class SimpleController {
def display() {
render template:"snippet"
}
}grails-app/views/simple/_snippet.gsp. You can test this as follows:void testDisplay() {
controller.display()
assert response.text == 'contents of template'
}void testDisplay() {
views['/simple/_snippet.gsp'] = 'mock contents'
controller.display()
assert response.text == 'mock contents'
}Testing XML and JSON Responses
XML and JSON response are also written directly to the response. Grails' mocking capabilities provide some conveniences for testing XML and JSON response. For example consider the following action:def renderXml() {
render(contentType:"text/xml") {
book(title:"Great")
}
}xml property of the response:void testRenderXml() {
controller.renderXml()
assert "<book title='Great'/>" == response.text
assert "Great" == response.xml.@title.text()
}xml property is a parsed result from Groovy's XmlSlurper class which is very convenient for parsing XML.Testing JSON responses is pretty similar, instead you use the json property:// controller action
def renderJson() {
render(contentType:"text/json") {
book = "Great"
}
}// test
void testRenderJson() { controller.renderJson() assert '{"book":"Great"}' == controller.response.text
assert "Great" == response.json.book
}json property is an instance of org.codehaus.groovy.grails.web.json.JSONElement which is a map-like structure that is useful for parsing JSON responses.Testing Spring Beans
When usingTestFor only a subset of the Spring beans available to a running Grails application are available. If you wish to make additional beans available you can do so with the defineBeans method of GrailsUnitTestMixin:class SimpleController {
SimpleService simpleService
def hello() {
render simpleService.sayHello()
}
}void testBeanWiring() {
defineBeans {
simpleService(SimpleService)
} controller.hello() assert response.text == "Hello World"
}void testAutowiringViaNew() {
defineBeans {
simpleService(SimpleService)
} def controller1 = new SimpleController()
def controller2 = new SimpleController() assert controller1.simpleService != null
assert controller2.simpleService != null
}Testing Mime Type Handling
You can test mime type handling and thewithFormat method quite simply by setting the request's format attribute:// controller action
def sayHello() {
def data = [Hello:"World"]
withFormat {
xml { render data as XML }
html data
}
}// test
void testSayHello() {
request.format = 'xml'
controller.sayHello() assert '<?xml version="1.0" encoding="UTF-8"?><map><entry key="Hello">World</entry></map>' == response.text
}Testing Duplicate Form Submissions
Testing duplicate form submissions is a little bit more involved. Say for example you have an action that handles a form such as:def handleForm() {
withForm {
render "Good"
}.invalidToken {
render "Bad"
}
}void testDuplicateFormSubmission() {
controller.handleForm()
assert "Bad" == response.text
}SynchronizerTokenHolder:import org.codehaus.groovy.grails.web.servlet.mvc.SynchronizerTokenHolder ...void testValidFormSubmission() { def holder = SynchronizerTokensHolder.store(session) def token = holder.generateToken('/test') params[SynchronizerTokensHolder.TOKEN_URI] = '/test' params[SynchronizerTokensHolder.TOKEN_KEY] = token controller.handleForm() assert "Good" == response.text }
controller.handleForm() // first execution … response.reset() … controller.handleForm() // second execution
Testing File Upload
To test file uploads you need to use theGrailsMockMultipartFile class. For example consider the following controller action:def uploadFile() {
MultipartFile file = request.getFile("myFile")
file.transferTo(new File("/local/disk/myFile"))
}GrailsMockMultipartFile with the request:void testFileUpload() {
final file = new GrailsMockMultipartFile("myFile", "foo".bytes)
request.addFile(file)
controller.uploadFile() assert file.targetFileLocation.path == "/local/disk/myFile"
}GrailsMockMultipartFile takes the name of the file and the contents of the file as arguments to the constructor. It has a mock implementation of the transferTo method that simply records the targetFileLocation and doesn't write to disk.Testing Command Objects
Special support exists for testing command object handling via themockCommandObject method. For example consider the following action:def handleCommand = { SimpleCommand simple ->
if(simple.hasErrors()) {
render "Bad"
}
else {
render "Good"
}
}void testInvalidCommand() {
def cmd = mockCommandObject(SimpleCommand)
cmd.name = '' // doesn't allow blank names cmd.validate()
controller.handleCommand(cmd) assert response.text == 'Bad'
}Testing Calling Tag Libraries
You can test calling tag libraries usingControllerUnitTestMixin, although the mechanism for testing the tag called varies from tag to tag. For example to test a call to the message tag you need to add a message to the messageSource. For example consider the following action:def showMessage() {
render g.message(code:"foo.bar")
}void testRenderBasicTemplateWithTags() {
messageSource.addMessage("foo.bar", request.locale, "World") controller.showMessage() assert response.text == "Hello World"
}9.1.2 Unit Testing Tag Libraries
The Basics
Tag libraries and GSP pages can be tested with thegrails.test.mixin.web.GroovyPageUnitTestMixin test mixin. To use the mixin declare which tag library is under test with the TestFor annotation:@TestFor(SimpleTagLib)
class SimpleTagLibTests {
}ControllerUnitTestMixin and the GroovyPageUnitTestMixin using the Mock annotation:@TestFor(SimpleController)
@Mock(SimpleTagLib)
class GroovyPageUnitTestMixinTests {}Testing Custom Tags
The core Grails tags don't need to be enabled during testing, however custom tag libraries do. TheGroovyPageUnitTestMixin class provides a mockTagLib(Class) method that you can use to mock a custom tag library. For example consider the following tag library:class SimpleTagLib {
static namespace = 's' def hello = { attrs, body ->
out << "Hello ${attrs.name ?: 'World'}"
}
}TestFor and supplying the name of the tag library:@TestFor(SimpleTagLib)
class SimpleTagLibTests {
void testHelloTag() {
assert applyTemplate('<s:hello />') == 'Hello World'
assert applyTemplate('<s:hello name="Fred" />') == 'Hello Fred'
}
}TestMixin annotation and mock multiple tag libraries via the mockTagLib(Class) method:@grails.test.mixin.TestMixin(GroovyPageUnitTestMixin)
class MultipleTagLibraryTests { @Test
void testMuliple() {
mockTagLib(FirstTagLib)
mockTagLib(SecondTagLib) …
}
}Testing View and Template Rendering
You can test rendering of views and templates ingrails-app/views via the render(Map) method provided by GroovyPageUnitTestMixin :def result = render(template:"/simple/hello") assert result == "Hello World"
grails-app/views/simple/_hello.gsp. Note that if the template depends on any custom tag libraries you will need to call mockTagLib as described in the previous section.
9.1.3 Unit Testing Domains
Overview
Domain class interaction can be tested without involving a database connection using theDomainClassUnitTestMixin. This implementation mimics the behavior of GORM against an in-memory ConcurrentHashMap implementation. Note that it contains limitations in comparison to a real GORM implementation. The following features of GORM for Hibernate can only be tested within an integration test:
- String-based HQL queries
- Composite Identifiers
- Dirty Checking Methods
- Any Direct Interaction with Hibernate
DomainClassUnitTestMixin including:
- Simple persistence methods like save(), delete() etc.
- Dynamic Finders
- Named Queries
- Query-by-example
- GORM Events
GrailsUnitTestMixin's mockFor method can come in handy to mock the missing pieces. Alternatively you can write an integration test which bootstraps the complete Grails environment at a cost of test execution time.The Basics
DomainClassUnitTestMixin is typically used in combination with testing either a controller, service of tag library where the domain is a mock collaborator defined via the Mock annotation:import grails.test.mixin.*@TestFor(SimpleController)
@Mock(Simple)
class SimpleControllerTests {}SimpleController class and mocks the behavior of the Simple domain class as well. For example consider a typical scaffolded save controller action:class BookController {
def save = {
def book = new Book(params)
if (book.save(flush: true)) {
flash.message = message(
code: 'default.created.message',
args: [message(code: 'book.label', default: 'Book'), book.id])}"
redirect(action: "show", id: book.id)
}
else {
render(view: "create", model: [bookInstance: book])
}
}
}import grails.test.mixin.*@TestFor(BookController) @Mock(Book) class BookControllerTests { void testSaveInvalidBook() { controller.save() assert model.bookInstance != null assert view == '/book/create' } void testSaveValidBook() { params.title = "The Stand" params.pages = "500" controller.save() assert response.redirectedUrl == '/book/show/1' assert flash.message != null assert Book.count() == 1 } }
Mock annotation takes a list of mock collaborators:@TestFor(BookController)
@Mock([Book, Author])
class BookControllerTests {
…
}DomainClassUnitTestMixin directly via the TestMixin annotation:@TestFor(BookController)
@TestMixin(DomainClassUnitTestMixin)
class BookControllerTests {
…
}mockDomain method to mock domains during your test:void testSave() {
mockDomain(Author)
mockDomain(Book)
}mockDomain method also includes an additional parameter that allows you to pass a map of maps to setup an domain, which is useful for fixture-like data:void testSave() {
mockDomain(Book, [
[title:"The Stand", pages:"1000"],
[title:"The Shining", pages:400],
[title:"Along Came a Spider", pages:300] ])
}Testing Constraints
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 method 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)
}
}@TestFor(Book)
class BookTests {
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() assert !book.validate()
assert "nullable" == book.errors["title"]
assert "nullable" == book.errors["author"] // So let's demonstrate the unique and minSize constraints. book = new Book(title: "Misery", author: "JK")
assert !book.validate()
assert "unique" == book.errors["title"] assert "minSize" == book.errors["author"] // Validation should pass!
book = new Book(title: "The Shining", author: "Stephen King")
assert 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.
9.1.4 Unit Testing Filters
Unit testing filters is typically a matter of testing a controller where a filter is a mock collaborator. For example consider the following filters class:class CancellingFilters {
def filters = {
all(controller:"simple", action:"list") {
before = {
redirect(controller:"book")
return false
}
}
}
}list action of the simple controller and redirects to the book controller. To test this filter you start off with a test that targets the SimpleController class and add the CancellingFilters as a mock collaborator:@TestFor(SimpleController)
@Mock(CancellingFilters)
class SimpleControllerTests {}withFilters method to wrap the call to an action in filter execution:void testInvocationOfListActionIsFiltered() {
withFilters(action:"list") {
controller.list()
}
assert response.redirectedUrl == '/book'}action parameter is required because it is unknown what the action to invoke is until the action is actually called. The controller parameter is optional and taken from the controller under test. If it is a another controller you are testing then you can specify it:withFilters(controller:"book",action:"list") { controller.list() }
9.1.5 Unit Testing URL Mappings
The Basics
Testing URL mappings can be done via theTestFor annotation testing a particular URL mappings class. For example to test the default URL mappings you can do the following:@TestFor(UrlMappings)
class UrlMappingsTests {}
Note that since the default UrlMappings class is in the default package your test will need to be in the default package too
With that done there are a number of useful methods that are defined by the grails.test.mixin.web.UrlMappingsUnitTestMixin for testing URL mappings. These include:
assertUrlMapping- Asserts a URL mappings is valid for the given URL. This combines theassertForwardUrlMappingandassertReverseUrlMappingassertionsassertForwardUrlMapping- Asserts a URL mapping is forwarded for the given controller class (note that controller will need to be defined as a mock collaborate for this to work)assertReverseUrlMapping- Asserts that the given URL is produced when reverse mapping a link to a given controller and action
Asserting Forward URL Mappings
If you simply want to assert that a given URL maps to a given controller thenassertForwardUrlMapping can help. For example, take the following URL mappings:static mappings = { "/action1"(controller: "simple", action: "action1") "/action2"(controller: "simple", action: "action2") }
void testUrlMappings() {
assertForwardUrlMapping("/action1", controller:'simple', action: "action1")
assertForwardUrlMapping("/action2", controller:'simple', action: "action2")
shouldFail {
assertForwardUrlMapping("/action2", controller:'simple', action: "action1")
}
}Assert Reverse URL Mappings
If you want to check that the right links are produced for your URL mapping when using thelink tag in GSP views then assertReverseUrlMapping can be used. An example test is largely identical to the previous listing except you use assertReverseUrlMapping instead of assertForwardUrlMapping. Note that you can combine these 2 assertions with assertUrlMapping.Simulating Controller Mapping
In addition to the assertions to assert the validity of URL mappings you can also simulate mapping to a controller by using yourUrlMappings as a mock collaborator and the mapURI method. For example:@TestFor(SimpleController)
@Mock(UrlMappings)
class SimpleControllerTests {
void testControllerMapping() {
SimpleController controller = mapURI('/simple/list') assert controller != null
def model = controller.list() assert model != null
}
}9.1.6 Mocking Collaborators
Beyond the specific targeted mocking APIs there is also a all-purposemockFor() method that is available whenever using the TestFor annotation. The signature of the mockFor is: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)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.bookSimulating 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>
'''.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 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() } // register the original subflow registerFlow("example/sub", new ExampleController().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.

