Grails Test Mixins Plugin

Original Grails Testing Plugin based on @TestMixin

Version: 3.3.1.BUILD-SNAPSHOT

1 Introduction

This project provides the now deprecated @TestMixin unit testing framework for Grails 3.x which has been replaced by the new Trait-based Grails Test Support project.

This project mainly exists for backwards compatibility and to ease upgrades for existing users.

To install the test mixin library add the following dependency to the dependencies block of your build.gradle in a Grails application or plugin:

testCompile "org.grails:grails-test-mixins:3.3.1.BUILD-SNAPSHOT"

2 Unit Testing Controllers

The Basics

You use the grails.test.mixin.TestFor annotation to unit test controllers. Using TestFor in this manner activates the grails.test.mixin.web.ControllerUnitTestMixin and its associated API. For example:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void "test something"() {
    }
}

Adding the TestFor annotation to a controller causes a new controller field to be 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 any of your test method don’t start with "test" just add this manually

To test the simplest "Hello World"-style example you can do the following:

// Test class
class SimpleController {
    def hello() {
        render "hello"
    }
}
import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void "test hello"() {
        when:
        controller.hello()

        then:
        response.text == 'hello'
    }
}

The response object is an instance of GrailsMockHttpServletResponse (from the package org.codehaus.groovy.grails.plugins.testing) which extends Spring’s MockHttpServletResponse class and has a number of useful methods for inspecting the state of the response.

For example to test a redirect you can use the redirectedUrl property:

class SimpleController {
    def index() {
        redirect action: 'hello'
    }
    ...
}
import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test index'() {
        when:
        controller.index()

        then:
        response.redirectedUrl == '/simple/hello'
    }
}

Many actions make use of the parameter data associated with the request. For example, the 'sort', 'max', and 'offset' parameters are quite common. Providing these in the test is as simple as adding appropriate values to a special params variable:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(PersonController)
class PersonControllerSpec extends Specification {

    void 'test list'() {
        when:
        params.sort = 'name'
        params.max = 20
        params.offset = 0
        controller.list()

        then:
        // ...
    }
}

You can even control what type of request the controller action sees by setting the method property of the mock request:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(PersonController)
class PersonControllerSpec extends Specification {

    void 'test save'() {
        when:
        request.method = 'POST'
        controller.save()

        then:
        // ...
    }
}

This is particularly important if your actions do different things depending on the type of the request. Finally, you can mark a request as AJAX like so:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(PersonController)
class PersonControllerSpec extends Specification {

    void 'test list'() {
        when:
        request.method = 'POST'
        request.makeAjaxRequest()
        controller.getPage()

        then:
        // ...
    }
}

You only need to do this though if the code under test uses the xhr property on the request.

Testing View Rendering

To test view rendering you can inspect the state of the controller’s modelAndView property (an instance of org.springframework.web.servlet.ModelAndView) or you can use the view and model properties provided by the mixin:

class SimpleController {
    def home() {
        render view: "homePage", model: [title: "Hello World"]
    }
    ...
}
import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test home'() {
        when:
        controller.home()

        then:
        view == '/simple/homePage'
        model.title == 'Hello World'
    }
}

Note that the view string is the absolute view path, so it starts with a '/' and will include path elements, such as the directory named after the action’s controller.

Testing Template Rendering

Unlike view rendering, template rendering will actually attempt to write the template directly to the response rather than returning a ModelAndView hence it requires a different approach to testing.

Consider the following controller action:

class SimpleController {
    def display() {
        render template:"snippet"
    }
}

In this example the controller will look for a template in grails-app/views/simple/_snippet.gsp. You can test this as follows:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test display'() {
        when:
        controller.display()

        then:
        response.text == 'contents of the template'
    }
}

However, you may not want to render the real template, but just test that it was rendered. In this case you can provide mock Groovy Pages:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test display with mock template'() {
        when:
        views['/simple/_snippet.gsp'] = 'mock template contents'
        controller.display()

        then:
        response.text == 'mock template contents'
    }
}

Testing Actions Which Return A Map

When a controller action returns a java.util.Map that Map may be inspected directly to assert that it contains the expected data:

class SimpleController {
    def showBookDetails() {
        [title: 'The Nature Of Necessity', author: 'Alvin Plantinga']
    }
}
import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test show book details'() {
        when:
        def model = controller.showBookDetails()

        then:
        model.author == 'Alvin Plantinga'
    }
}

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")
    }
}

This can be tested using the xml property of the response:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test render xml'() {
        when:
        controller.renderXml()

        then:
        response.text == "<book title='Great'/>"
        response.xml.@title.text() == 'Great'
    }
}

The 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:"application/json") {
        book "Great"
    }
}
import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test render json'() {
        when:
        controller.renderJson()

        then:
        response.text == '{"book":"Great"}'
        response.json.book == 'Great'
    }
}

The 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 XML and JSON Requests

Grails provides various convenient ways to automatically parse incoming XML and JSON packets. For example you can bind incoming JSON or XML requests using Grails' data binding:

def consumeBook(Book b) {
    render "The title is ${b.title}."
}

To test this Grails provides an easy way to specify an XML or JSON packet via the xml or json properties. For example the above action can be tested by specifying a String containing the XML:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(SimpleController)
@Mock([Book])
class SimpleControllerSpec extends Specification {
    void 'test consume book xml'() {
        when:
        request.xml = '<book><title>Wool</title></book>'
        controller.consumeBook()

        then:
        response.text == 'The title is Wool.'
    }
}

Or alternatively a domain instance can be specified and it will be auto-converted into the appropriate XML request:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(SimpleController)
@Mock([Book])
class SimpleControllerSpec extends Specification {

    void 'test consume book xml'() {
        when:
        request.xml = new Book(title: 'Shift')
        controller.consumeBook()

        then:
        response.text == 'The title is Shift.'
    }
}

The same can be done for JSON requests:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(SimpleController)
@Mock([Book])
class SimpleControllerSpec extends Specification {

    void 'test consume book json'() {
        when:
        request.json = new Book(title: 'Shift')
        controller.consumeBook()

        then:
        response.text == 'The title is Shift.'
    }
}

If you prefer not to use Grails' data binding but instead manually parse the incoming XML or JSON that can be tested too. For example consider the controller action below:

def consume() {
    request.withFormat {
        xml {
            render "The XML Title Is ${request.XML.@title}."
        }
        json {
            render "The JSON Title Is ${request.JSON.title}."
        }
    }
}

To test the XML request you can specify the XML as a string:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test consume xml'() {
        when:
        request.xml = '<book title="The Stand"/>'
        controller.consume()

        then:
        response.text == 'The XML Title Is The Stand.'
    }

    void 'test consume json'() {
        when:
        request.json = '{title:"The Stand"}'
        controller.consume()

        then:
        response.text == 'The JSON Title Is The Stand.'
    }
}

Testing Mime Type Handling

You can test mime type handling and the withFormat method quite simply by setting the request’s contentType attribute:

// controller action
def sayHello() {
    def data = [Hello:"World"]
    request.withFormat {
        xml { render data as grails.converters.XML }
        json { render data as grails.converters.JSON }
        html data
    }
}
import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test say hello xml'() {
        when:
        request.contentType = 'application/xml'
        controller.sayHello()

        then:
        response.text == '<?xml version="1.0" encoding="UTF-8"?><map><entry key="Hello">World</entry></map>'
    }

    void 'test say hello json'() {
        when:
        request.contentType = 'application/json'
        controller.sayHello()

        then:
        response.text == '{"World"}'[Hello]     }
}

There are constants provided by ControllerUnitTestMixin for all of the common common content types as shown below:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test say hello xml'() {
        when:
        request.contentType = XML_CONTENT_TYPE
        controller.sayHello()

        then:
        response.text == '<?xml version="1.0" encoding="UTF-8"?><map><entry key="Hello">World</entry></map>'
    }

    void 'test say hello json'() {
        when:
        request.contentType = JSON_CONTENT_TYPE
        controller.sayHello()

        then:
        response.text == '{"World"}'[Hello]     }
}

The defined constants are listed below:

Constant Value

ALL_CONTENT_TYPE

*/\*

FORM_CONTENT_TYPE

application/x-www-form-urlencoded

MULTIPART_FORM_CONTENT_TYPE

multipart/form-data

HTML_CONTENT_TYPE

text/html

XHTML_CONTENT_TYPE

application/xhtml+xml

XML_CONTENT_TYPE

application/xml

JSON_CONTENT_TYPE

application/json

TEXT_XML_CONTENT_TYPE

text/xml

TEXT_JSON_CONTENT_TYPE

text/json

HAL_JSON_CONTENT_TYPE

application/hal+json

HAL_XML_CONTENT_TYPE

application/hal+xml

ATOM_XML_CONTENT_TYPE

application/atom+xml

Testing Duplicate Form Submissions

Testing duplicate form submissions is a little bit more involved. For example if you have an action that handles a form such as:

def handleForm() {
    withForm {
        render "Good"
    }.invalidToken {
        render "Bad"
    }
}

you want to verify the logic that is executed on a good form submission and the logic that is executed on a duplicate submission. Testing the bad submission is simple. Just invoke the controller:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test duplicate form submission'() {
        when:
        controller.handleForm()

        then:
        response.text == 'Bad'
    }
}

Testing the successful submission requires providing an appropriate SynchronizerToken:

import grails.test.mixin.TestFor
import spock.lang.Specification

import org.codehaus.groovy.grails.web.servlet.mvc.SynchronizerTokensHolder

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test valid form submission'() {
        when:
        def tokenHolder = SynchronizerTokensHolder.store(session)

        params[SynchronizerTokensHolder.TOKEN_URI] = '/controller/handleForm'
        params[SynchronizerTokensHolder.TOKEN_KEY] = tokenHolder.generateToken(params[SynchronizerTokensHolder.TOKEN_URI])
        controller.handleForm()

        then:
        response.text == 'Good'
    }
}

If you test both the valid and the invalid request in the same test be sure to reset the response between executions of the controller:

import grails.test.mixin.TestFor
import spock.lang.Specification

import org.codehaus.groovy.grails.web.servlet.mvc.SynchronizerTokensHolder

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test form submission'() {
        when:
        controller.handleForm()

        then:
        response.text == 'Bad'

        when:
        response.reset()
        def tokenHolder = SynchronizerTokensHolder.store(session)

        params[SynchronizerTokensHolder.TOKEN_URI] = '/controller/handleForm'
        params[SynchronizerTokensHolder.TOKEN_KEY] = tokenHolder.generateToken(params[SynchronizerTokensHolder.TOKEN_URI])
        controller.handleForm()

        then:
        response.text == 'Good'
    }
}

Testing File Upload

You use the GrailsMockMultipartFile class to test file uploads. For example consider the following controller action:

def uploadFile() {
    MultipartFile file = request.getFile("myFile")
    file.transferTo(new File("/local/disk/myFile"))
}

To test this action you can register a GrailsMockMultipartFile with the request:

import grails.test.mixin.TestFor
import spock.lang.Specification

import org.codehaus.groovy.grails.plugins.testing.GrailsMockMultipartFile

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test file upload'() {
        when:
        def file = new GrailsMockMultipartFile('myFile', 'some file contents'.bytes)
        request.addFile file
        controller.uploadFile()

        then:
        file.targetFileLocation.path == '/local/disk/myFile'
    }
}

The GrailsMockMultipartFile constructor arguments are the name and contents of the file. 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 with the mockCommandObject method. For example consider the following action:

class SimpleController {
    def handleCommand(SimpleCommand simple) {
        if(simple.hasErrors()) {
            render 'Bad'
        } else {
            render 'Good'
        }
    }
}

class SimpleCommand {
    String name

    static constraints = {
        name blank: false
    }
}

To test this you mock the command object, populate it and then validate it as follows:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test valid command object'() {
        given:
        def simpleCommand = new SimpleCommand(name: 'Hugh')
        simpleCommand.validate()

        when:
        controller.handleCommand(simpleCommand)

        then:
        response.text == 'Good'
    }

    void 'test invalid command object'() {
        given:
        def simpleCommand = new SimpleCommand(name: '')
        simpleCommand.validate()

        when:
        controller.handleCommand(simpleCommand)

        then:
        response.text == 'Bad'
    }
}

The testing framework also supports allowing Grails to create the command object instance automatically. To test this invoke the no-arg version of the controller action method. Grails will create an instance of the command object, perform data binding on it using the request parameters and validate the object just like it does when the application is running. See the test below.

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test valid command object'() {
        when:
        params.name = 'Hugh'
        controller.handleCommand()

        then:
        response.text == 'Good'
    }

    void 'test invalid command object'() {
        when:
        params.name = ''
        controller.handleCommand()

        then:
        response.text == 'Bad'
    }
}

Testing allowedMethods

The unit testing environment respects the allowedMethods property in controllers. If a controller action is limited to be accessed with certain request methods, the unit test must be constructed to deal with that.

grails-app/controllers/com/demo/DemoController.groovy
package com.demo

class DemoController {

    static allowedMethods = [save: 'POST', update: 'PUT', delete: 'DELETE']

    def save() {
        render 'Save was successful!'
    }

    // ...
}
src/test/groovy/com/demo/DemoControllerSpec.groovy
package com.demo

import grails.test.mixin.TestFor
import spock.lang.Specification
import static javax.servlet.http.HttpServletResponse.*

@TestFor(DemoController)
class DemoControllerSpec extends Specification {

    void "test a valid request method"() {
        when:
        request.method = 'POST'
        controller.save()

        then:
        response.status == SC_OK
        response.text == 'Save was successful!'
    }

    void "test an invalid request method"() {
        when:
        request.method = 'DELETE'
        controller.save()

        then:
        response.status == SC_METHOD_NOT_ALLOWED
    }
}

Testing Calling Tag Libraries

You can test calling tag libraries using ControllerUnitTestMixin, although the mechanism for testing the tag called varies from tag to tag. For example to test a call to the message tag, add a message to the messageSource. Consider the following action:

def showMessage() {
    render g.message(code: "foo.bar")
}

This can be tested as follows:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {

    void 'test render message tag'() {
        given:
        messageSource.addMessage 'foo.bar', request.locale, 'Hello World'

        when:
        controller.showMessage()

        then:
        response.text == 'Hello World'
    }
}

See unit testing tag libraries for more information.

3 Unit Testing Tag Libraries

The Basics

Tag libraries and GSP pages can be tested with the grails.test.mixin.web.GroovyPageUnitTestMixin mixin. To use the mixin declare which tag library is under test with the TestFor annotation:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleTagLib)
class SimpleTagLibSpec extends Specification {

    void "test something"() {
    }
}

Adding the TestFor annotation to a TagLib class causes a new tagLib field to be automatically created for the TagLib class under test. The tagLib field can be used to test calling tags as function calls. The return value of a function call is either a StreamCharBuffer instance or the object returned from the tag closure when returnObjectForTags feature is used.

Note that if you are testing invocation of a custom tag from a controller you can combine the ControllerUnitTestMixin and the GroovyPageUnitTestMixin using the Mock annotation:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(SimpleController)
@Mock(SimpleTagLib)
class SimpleControllerSpec extends Specification {

}

Testing Custom Tags

The core Grails tags don’t need to be enabled during testing, however custom tag libraries do. The GroovyPageUnitTestMixin class provides a mockTagLib() 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'}"
    }

    def bye = { attrs, body ->
        out << "Bye ${attrs.author.name ?: 'World'}"
    }
}

You can test this tag library by using TestFor and supplying the name of the tag library:

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleTagLib)
class SimpleTagLibSpec extends Specification {

    void "test hello tag"() {
        expect:
        applyTemplate('<s:hello />') == 'Hello World'
        applyTemplate('<s:hello name="Fred" />') == 'Hello Fred'
        applyTemplate('<s:bye author="${author}" />', [author: new Author(name: 'Fred')]) == 'Bye Fred'
    }

    void "test tag calls"() {
        expect:
        tagLib.hello().toString() == 'Hello World'
        tagLib.hello(name: 'Fred').toString() == 'Hello Fred'
        tagLib.bye(author: new Author(name: 'Fred')).toString == 'Bye Fred'
    }
}

Alternatively, you can use the TestMixin annotation and mock multiple tag libraries using the mockTagLib() method:

import spock.lang.Specification
import grails.test.mixin.TestMixin
import grails.test.mixin.web.GroovyPageUnitTestMixin

@TestMixin(GroovyPageUnitTestMixin)
class MultipleTagLibSpec extends Specification {

    void "test multiple tags"() {
        given:
        mockTagLib(SomeTagLib)
        mockTagLib(SomeOtherTagLib)

        expect:
        // ...
    }
}

The GroovyPageUnitTestMixin provides convenience methods for asserting that the template output equals or matches an expected value.

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleTagLib)
class SimpleTagLibSpec extends Specification {

    void "test hello tag"() {
        expect:
        assertOutputEquals ('Hello World', '<s:hello />')
        assertOutputMatches (/.*Fred.*/, '<s:hello name="Fred" />')
    }
}

Testing View and Template Rendering

You can test rendering of views and templates in grails-app/views via the render(Map) method provided by GroovyPageUnitTestMixin :

import spock.lang.Specification
import grails.test.mixin.TestMixin
import grails.test.mixin.web.GroovyPageUnitTestMixin

@TestMixin(GroovyPageUnitTestMixin)
class RenderingSpec extends Specification {

    void "test rendering template"() {
        when:
        def result = render(template: '/simple/hello')

        then:
        result == 'Hello World!'
    }
}

This will attempt to render a template found at the location grails-app/views/simple/_hello.gsp. Note that if the template depends on any custom tag libraries you need to call mockTagLib as described in the previous section.

Some core tags use the active controller and action as input. In GroovyPageUnitTestMixin tests, you can manually set the active controller and action name by setting controllerName and actionName properties on the webRequest object:

webRequest.controllerName = 'simple'
    webRequest.actionName = 'hello'

4 Unit Testing Domains

Overview

Domain class interaction can be tested without involving a real database connection using DomainClassUnitTestMixin.

The GORM implementation in DomainClassUnitTestMixin is using a simple in-memory ConcurrentHashMap implementation. Note that this has limitations compared to a real GORM implementation.

A large, commonly-used portion of the GORM API can be mocked using DomainClassUnitTestMixin including:

  • Simple persistence methods like save(), delete() etc.

  • Dynamic Finders

  • Named Queries

  • Query-by-example

  • GORM Events

If you wish to test GORM using a real database then it is recommended to use one of the following super classes:

  • grails.test.hibernate.HibernateSpec - Sets up Hibernate for the current test

  • grails.test.mongodb.MongoSpec - Sets up MongoDB for the current test

All features of GORM for Hibernate can be tested within a HibernateSpec unit test including:

  • String-based HQL queries

  • composite identifiers

  • dirty checking methods

  • any direct interaction with Hibernate

The implementation behind HibernateSpec takes care of setting up the Hibernate with the in-memory H2 database.

DomainClassUnitTestMixin Basics

DomainClassUnitTestMixin is typically used in combination with testing either a controller, service or tag library where the domain is a mock collaborator defined by the Mock annotation:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(BookController)
@Mock(Book)
class BookControllerSpec extends Specification {
    // ...
}

The example above tests the BookController class and mocks the behavior of the Book 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])
        }
    }
}

Tests for this action can be written as follows:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(BookController)
@Mock(Book)
class BookControllerSpec extends Specification {
   void "test saving an invalid book"() {
        when:
        controller.save()

        then:
        model.bookInstance != null
        view == '/book/create'
    }

    void "test saving a valid book"() {
        when:
        params.title = "The Stand"
        params.pages = "500"

        controller.save()

        then:
        response.redirectedUrl == '/book/show/1'
        flash.message != null
        Book.count() == 1
    }
}

Mock annotation also supports a list of mock collaborators if you have more than one domain to mock:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(BookController)
@Mock([Book, Author])
class BookControllerSpec extends Specification {
    // ...
}

Alternatively you can also use the DomainClassUnitTestMixin directly with the TestMixin annotation and then call the mockDomain method to mock domains during your test:

import grails.test.mixin.TestFor
import grails.test.mixin.TestMixin
import spock.lang.Specification
import grails.test.mixin.domain.DomainClassUnitTestMixin

@TestFor(BookController)
@TestMixin(DomainClassUnitTestMixin)
class BookControllerSpec extends Specification {

    void setupSpec() {
         mockDomain(Book)
    }

    void "test saving an invalid book"() {
        when:
        controller.save()

        then:
        model.bookInstance != null
        view == '/book/create'
    }

    void "test saving a valid book"() {
        when:
        params.title = "The Stand"
        params.pages = "500"

        controller.save()

        then:
        response.redirectedUrl == '/book/show/1'
        flash.message != null
        Book.count() == 1
    }
}

The mockDomain method also includes an additional parameter that lets you pass a List of Maps to configure a domain, which is useful for fixture-like data:

mockDomain(Book, [
            [title: "The Stand", pages: 1000],
            [title: "The Shining", pages: 400],
            [title: "Along Came a Spider", pages: 300] ])

Testing Constraints

There are 3 types of validateable classes:

  • Domain classes

  • Classes which implement the Validateable trait

  • Command Objects which have been made validateable automatically

These are all easily testable in a unit test with no special configuration necessary as long as the test method is marked with TestFor or explicitly applies the GrailsUnitTestMixin using TestMixin. See the examples below.

src/main/groovy/com/demo/MyValidateable.groovy
package com.demo

class MyValidateable implements grails.validation.Validateable {
    String name
    Integer age

    static constraints = {
        name matches: /[A-Z].*/
        age range: 1..99
    }
}
grails-app/domain/com/demo/Person.groovy
package com.demo

class Person {
    String name

    static constraints = {
        name matches: /[A-Z].*/
    }
}
grails-app/controllers/com/demo/DemoController.groovy
package com.demo

class DemoController {

    def addItems(MyCommandObject co) {
        if(co.hasErrors()) {
            render 'something went wrong'
        } else {
            render 'items have been added'
        }
    }
}

class MyCommandObject {
    Integer numberOfItems

    static constraints = {
        numberOfItems range: 1..10
    }
}
test/unit/com/demo/PersonSpec.groovy
package com.demo

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(Person)
class PersonSpec extends Specification {

    void "Test that name must begin with an upper case letter"() {
        when: 'the name begins with a lower letter'
        def p = new Person(name: 'jeff')

        then: 'validation should fail'
        !p.validate()

        when: 'the name begins with an upper case letter'
        p = new Person(name: 'Jeff')

        then: 'validation should pass'
        p.validate()
    }
}
test/unit/com/demo/DemoControllerSpec.groovy
package com.demo

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(DemoController)
class DemoControllerSpec extends Specification {

    void 'Test an invalid number of items'() {
        when:
        params.numberOfItems = 42
        controller.addItems()

        then:
        response.text == 'something went wrong'
    }

    void 'Test a valid number of items'() {
        when:
        params.numberOfItems = 8
        controller.addItems()

        then:
        response.text == 'items have been added'
    }
}
test/unit/com/demo/MyValidateableSpec.groovy
package com.demo

import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import spock.lang.Specification


@TestMixin(GrailsUnitTestMixin)
class MyValidateableSpec extends Specification {

    void 'Test validate can be invoked in a unit test with no special configuration'() {
        when: 'an object is valid'
        def validateable = new MyValidateable(name: 'Kirk', age: 47)

        then: 'validate() returns true and there are no errors'
        validateable.validate()
        !validateable.hasErrors()
        validateable.errors.errorCount == 0

        when: 'an object is invalid'
        validateable.name = 'kirk'

        then: 'validate() returns false and the appropriate error is created'
        !validateable.validate()
        validateable.hasErrors()
        validateable.errors.errorCount == 1
        validateable.errors['name'].code == 'matches.invalid'

        when: 'the clearErrors() is called'
        validateable.clearErrors()

        then: 'the errors are gone'
        !validateable.hasErrors()
        validateable.errors.errorCount == 0

        when: 'the object is put back in a valid state'
        validateable.name = 'Kirk'

        then: 'validate() returns true and there are no errors'
        validateable.validate()
        !validateable.hasErrors()
        validateable.errors.errorCount == 0
    }
}
test/unit/com/demo/MyCommandObjectSpec.groovy
package com.demo

import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import spock.lang.Specification

@TestMixin(GrailsUnitTestMixin)
class MyCommandObjectSpec extends Specification {

    void 'Test that numberOfItems must be between 1 and 10'() {
        when: 'numberOfItems is less than 1'
        def co = new MyCommandObject()
        co.numberOfItems = 0

        then: 'validation fails'
        !co.validate()
        co.hasErrors()
        co.errors['numberOfItems'].code == 'range.toosmall'

        when: 'numberOfItems is greater than 10'
        co.numberOfItems = 11

        then: 'validation fails'
        !co.validate()
        co.hasErrors()
        co.errors['numberOfItems'].code == 'range.toobig'

        when: 'numberOfItems is greater than 1'
        co.numberOfItems = 1

        then: 'validation succeeds'
        co.validate()
        !co.hasErrors()

        when: 'numberOfItems is greater than 10'
        co.numberOfItems = 10

        then: 'validation succeeds'
        co.validate()
        !co.hasErrors()
    }
}

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 name which is a mistake that is easy to make and equally easy to overlook. A unit test for your constraints will highlight the problem straight away.

HibernateSpec and MongoSpec Basics

HibernateSpec allows Hibernate to be used in Grails unit tests. It uses a H2 in-memory database. The following documentation covers HibernateSpec, but the same concepts can be applied to MongoSpec.

package example

import grails.test.hibernate.HibernateSpec
import spock.lang.Specification


class PersonSpec extends HibernateSpec {

    void "Test count people"() {
        expect: "Test execute Hibernate count query"
            Person.count() == 0
            sessionFactory != null
            transactionManager != null
            hibernateSession != null
    }
}

By default HibernateSpec will scan for domain classes within the package specified by the grails.codegen.defaultPackage configuration option or if not specified the same package as the test.

Configuring domain classes for HibernateSpec tests

If you only wish to test a limited set of domain classes you can override the getDomainClasses method to specify exactly which ones you wish to test:

package example

import grails.test.hibernate.HibernateSpec

class PersonSpec extends HibernateSpec {
    ...

    List<Class> getDomainClasses() { [ Person ] }
}

Testing Controllers and Services with HibernateSpec

If you wish to test a controller or a service with HibernateSpec generally you can combine HibernateSpec with Grails' default test mixins:

package example

import grails.test.hibernate.HibernateSpec

@TestFor(BookController)
class BookControllerUnitSpec extends HibernateSpec {

    ...
}

However, if the controller or service uses @Transactional you will need to assign the transaction manager within the unit test’s setup method:

def setup() {
    controller.transactionManager = transactionManager
}

5 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
            }
        }
    }
}

This filter interceptors the 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:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(SimpleController)
@Mock(CancellingFilters)
class SimpleControllerSpec extends Specification {

    // ...

}

You can then implement a test that uses the withFilters method to wrap the call to an action in filter execution:

import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(SimpleController)
@Mock(CancellingFilters)
class SimpleControllerSpec extends Specification {

    void "test list action is filtered"() {
        when:
        withFilters(action:"list") {
            controller.list()
        }

        then:
        response.redirectedUrl == '/book'
    }
}

Note that the 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 another controller you are testing then you can specify it:

withFilters(controller:"book",action:"list") {
    controller.list()
}

6 Unit Testing URL Mappings

The Basics

Testing URL mappings can be done with the TestFor annotation testing a particular URL mappings class. For example to test the default URL mappings you can do the following:

import com.demo.SimpleController
import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(UrlMappings)
@Mock(SimpleController)
class UrlMappingsSpec extends Specification {
    // ...
}

As you can see, any controller that is the target of a URL mapping that you’re testing must be added to the @Mock annotation.

Note that since the default UrlMappings class is in the default package your test must also be in the default package

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:

  • assertForwardUrlMapping - 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

  • assertUrlMapping - Asserts a URL mapping is valid for the given URL. This combines the assertForwardUrlMapping and assertReverseUrlMapping assertions

Asserting Forward URL Mappings

You use assertForwardUrlMapping to assert that a given URL maps to a given controller. For example, consider the following URL mappings:

static mappings = {
    "/actionOne"(controller: "simple", action: "action1")
    "/actionTwo"(controller: "simple", action: "action2")
}

The following test can be written to assert these URL mappings:

import com.demo.SimpleController
import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification

@TestFor(UrlMappings)
@Mock(SimpleController)
class UrlMappingsSpec extends Specification {

    void "test forward mappings"() {
        expect:
        assertForwardUrlMapping("/actionOne", controller: 'simple', action: "action1")
        assertForwardUrlMapping("/actionTwo", controller: 'simple', action: "action2")
    }
}

Assert Reverse URL Mappings

You use assertReverseUrlMapping to check that correct links are produced for your URL mapping when using the link tag in GSP views. 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.

7 Mocking Collaborators

The Spock Framework manual has a chapter on Interaction Based Testing which also explains mocking collaborators.

8 Mocking Codecs

The GrailsUnitTestMixin provides a mockCodec method for mocking custom codecs which may be invoked while a unit test is running.

mockCodec(MyCustomCodec)

Failing to mock a codec which is invoked while a unit test is running may result in a MissingMethodException.

9 Unit Test Metaprogramming

If runtime metaprogramming needs to be done in a unit test it needs to be done early in the process before the unit testing environment is fully initialized. This should be done when the unit test class is being initialized. For a Spock based test this should be done in the setupSpec() method. For a JUnit test this should be done in a method marked with @BeforeClass.

package myapp

import grails.test.mixin.*
import spock.lang.Specification

@TestFor(SomeController)
class SomeControllerSpec extends Specification {


    def setupSpec() {
        SomeClass.metaClass.someMethod = { ->
            // do something here
        }
    }

    // ...
}
package myapp

import grails.test.mixin.*
import org.junit.*

@TestFor(SomeController)
class SomeControllerTests {

    @BeforeClass
    static void metaProgramController() {
        SomeClass.metaClass.someMethod = { ->
            // do something here
        }
    }

    // ...

}