(Quick Reference)

Grails Testing Support

Authors: Jeff Brown,James Kleeh

Version: 3.1.2

1 Introduction

The Grails Testing Support library provides support for writing concise expressive tests for Grails artifacts with simple, easy to use traits and supporting classes. The use of Traits makes functionality woven into the test class at compile time, which allows developers to explicitly view and explore the code. In addition, the Grails Testing Support library makes testing more IDE-friendly so developers can take advantage of auto-completion and click through navigation.

2 Installation

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

testCompile "org.grails:grails-gorm-testing-support:3.1.2"
testCompile "org.grails:grails-web-testing-support:3.1.2"
The dependencies are optional. If you are not unit testing domain activity, you may not need the GORM testing support library.

3 Upgrading From The Mixin Framework

This library was designed to be compatible with the old mixin approach. There are some slight differences that you may encounter, however any required changes should be minimal.

FreshRuntime Removed

The @FreshRuntime annotation was removed due to the design of the new framework. The annotation allowed any metaclass changes to be sandboxed either at the method or class level. Spock provides a similar annotation to do the same thing, @ConfineMetaClassChanges.

In addition, the annotation caused the application context to be refreshed. Because the application context is refreshed between test classes automatically, the annotation is no longer necessary.

Integration Tests

The @Integration annotation was copied to this library so a dependency on the old framework is no longer necessary. The package has changed from grails.test.mixin.integration.Integration to grails.testing.mixin.integration.Integration. Everything about integration tests should work the same as before.

Example Converted Test

Here is an example test that may look like something in your project.

@TestFor(AuthorController)
@Mock(Author)
class AuthorControllerTests {

    @Test
    void testIndex() {
        controller.index()
        assert response.text == 'Hello'
    }
}

To convert this test to the new framework, we must update it to use Spock as well as change the annotation usages to trait usages.

import spock.lang.Specification
import grails.testing.gorm.DomainUnitTest
import grails.testing.web.controllers.ControllerUnitTest

class AuthorControllerTests extends Specification implements ControllerUnitTest<AuthorController>, DomainUnitTest<Author> {

    void testIndex() {
        when:
        controller.index()

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

Obviously there are many use cases you may want to convert and it would be impossible to cover them all in this documentation. As a part of the testing of this new framework, all of the usages of the old framework were replaced in Grails core. You can view those changes by looking at this commit to see the vast majority of examples you will need.

4 Unit Testing

By implementing Grails Testing Support traits, unit tests inherit functionality that make writing tests concise and simple.

The following functionality is available to all tests

Modifying the Application Context

Any modifications to the context will be specific to that test class. A new context is created for each test class. Artifacts under test are subjected to dependency injection just like they are in the running application. The unit testing environment does not spin up the entire Spring application context with all of the beans that would normally be configured in the running application. Unit tests may register any beans necessary to carry out the test in a number of different ways.

doWithSpring

To provide or replace beans in the context, you can override the doWithSpring method in your test.

Closure doWithSpring() {{ ->
    someService(SomeService)
}}
The syntax available in this closure is the same syntax that may be used in grails-app/conf/spring/resources.groovy for defining bean definitions.

resources.groovy

If you want your application’s resources file to be loaded into the context, override the loadExternalBeans method.

boolean loadExternalBeans() {
    true
}

Spring configuration from plugins

If you would like the doWithSpring configuration of any loaded plugins to be invoked for your tests, override the getIncludePlugins method and return a list of strings representing the plugin names.

Set<String> getIncludePlugins() {
    ["springSecurityCore"].toSet()
}
If you override this method, the default plugins will not be included. The default plugins are core and eventBus. If you want to add a plugin in addition to the defaults, add the defaults to your list.

Other Spring configuration

At any time during your tests, you can also directly call the defineBeans method. The defineBeans method can either take a closure, or an instance of a plugin.

void "test the bean is available to the context"() {
    given:
    defineBeans {
        someInteger(Integer, 2)
    }

    expect:
    applicationContext.getBean('someInteger') == 2
}

If you pass a plugin instance to the method, the doWithSpring will be executed.

void testSomething() {
    given:
    defineBeans(new MyCustomPlugin())

    expect:
    applicationContext.containsBean('someBeanAddedByDoWithSpring')
}

Autowiring The Test

It is possible to set up the test class itself for autowiring. Simply implement the AutowiredTest trait, and beans will be injected into the test class.

For Example:

import grails.testing.spring.AutowiredTest
import spock.lang.Specification

class AutowiredTestSpec extends Specification implements AutowiredTest {

    Closure doWithSpring() {{ ->
        helperService HelperService
    }}

    HelperService helperService

    void setup() {
        assert helperService != null
    }

    void 'some test method'() {
        expect:
        helperService != null
    }

    void 'some other test method'() {
        expect:
        helperService != null
    }
}

Manipulating Configuration

To change configuration for the context of your test class, override the doWithConfig method.

Closure doWithConfig() {{ config ->
    config.foo.bar = "x"
}}
A test only needs to implement the GrailsUnitTest trait to get the above functionality. All of the other testing traits extend GrailsUnitTest, so implementing it directly is uncommon.

4.1 Unit Testing Controllers

Use the grails.testing.web.controllers.ControllerUnitTest trait to unit test controllers.

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

import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification

class DemoControllerSpec extends Specification implements ControllerUnitTest<DemoController> {

    // ...

}

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

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

class DemoController {

    def hello() {
        render 'Hello, World!'
    }

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

import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification

class DemoControllerSpec extends Specification implements ControllerUnitTest<DemoController> {

    // ...

    void "test action which renders text"() {
        when:
        controller.hello()               (1)

        then:
        status == 200                    (2)
        response.text == 'Hello, World!' (3)
    }

}
1 The controller property will be an instance of DemoController
2 The status property will contain the value of the response status
3 The response property will be a reference to the HTTP response object

See the ControllerUnitTest docs for information on all of the available properties.

In an effort to make testing controllers that render JSON views easy, a change was made that required the controller variable used in the test code to be a proxy that delegates to the real controller instance. If for some reason that causes an issue with your test, it is possible to disable the creation of a proxy by overriding a method.
class DemoControllerSpec implements ControllerUnitTest<DemoController> {

    boolean disableControllerProxy() {
        true
    }
}

By doing so, JSON views will not be rendered by default. To enable JSON views to render automatically as before, it is necessary to inform the webRequest object which action you are invoking.

class DemoControllerSpec implements ControllerUnitTest<DemoController> {

    void "test index"() {
        when:
        webRequest.actionName = 'index'
        controller.index()

        then:
        ...
    }

    boolean disableControllerProxy() {
        true
    }
}

4.2 Unit Testing Domain Classes

Use the grails.testing.gorm.DomainUnitTest trait to unit test single domain class.

grails-app/domain/demo/Person.groovy
package demo

class Person {
    String firstName
    String lastName
}
src/test/groovy/demo/PersonSpec.groovy
package demo

import grails.testing.gorm.DomainUnitTest
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Stepwise

@Stepwise
class PersonSpec extends Specification implements DomainUnitTest<Person> {

    @Shared int id

    void "test basic persistence mocking"() {
        setup:
        new Person(firstName: 'Robert', lastName: 'Fripp').save()
        new Person(firstName: 'Adrian', lastName: 'Belew').save()

        expect:
        Person.count() == 2
    }

    void "test domain instance"() {
        setup:
        id = System.identityHashCode(domain)

        expect:
        domain != null
        domain.hashCode() == id

        when:
        domain.firstName = 'Robert'

        then:
        domain.firstName == 'Robert'
    }

    void "test we get a new domain"() {
        expect:
        domain != null
        domain.firstName == null
        System.identityHashCode(domain) != id
    }
}

Alternatively, the grails.testing.gorm.DataTest trait may be used. When using DataTest, an explicit call to the mockDomain or mockDomains method may be used to specify which domain class(es) should be mocked for this test. This is useful when mocking more than one Domain class at a time to test persistence.

src/test/groovy/demo/DataTestTraitSpec.groovy
package demo

import grails.testing.gorm.DataTest
import spock.lang.Specification

class DataTestTraitSpec extends Specification implements DataTest {

    void setupSpec() {
        mockDomain Person

        // for multiple domains, call mockDomains...
        // mockDomains Person, Address, Company
    }

    void "test basic persistence mocking"() {
        setup:
        new Person(firstName: 'Robert', lastName: 'Fripp').save()
        new Person(firstName: 'Adrian', lastName: 'Belew').save()

        expect:
        Person.count() == 2
    }
}

Another way to express which domain classes should be mocked for this test is to provide a Class[] getDomainClassesToMock() method in the test.

src/test/groovy/demo/GetDomainClassesToMockMethodSpec.groovy
package demo

import grails.testing.gorm.DataTest
import spock.lang.Specification

class GetDomainClassesToMockMethodSpec extends Specification implements DataTest {

    Class[] getDomainClassesToMock() {
        Person
    }

    void "test basic persistence mocking"() {
        setup:
        new Person(firstName: 'Robert', lastName: 'Fripp').save()
        new Person(firstName: 'Adrian', lastName: 'Belew').save()

        expect:
        Person.count() == 2
    }
}

When mocking domain classes in a test for another artifact type (like a ControllerUnitTest test, for example), the test must implement the DataTest trait in order to mock the related domain classes.

src/test/groovy/demo/PersonControllerSpec.groovy
package demo

import grails.testing.gorm.DataTest
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification

class PersonControllerSpec extends Specification implements ControllerUnitTest<PersonController>, DataTest {

    void setupSpec() {
        mockDomain Person
    }

    void "test action which invokes GORM method"() {
        setup:
        new Person(firstName: 'Robert', lastName: 'Fripp').save()
        new Person(firstName: 'Adrian', lastName: 'Belew').save()

        when:
        def model = controller.index()

        then:
        model.people.size() == 2
        model.keySet().contains('people')
    }
}

4.3 Unit Testing Services

Use the grails.testing.services.ServiceUnitTest trait to unit test services.

grails-app/services/demo/HelperService.groovy
package demo

class HelperService {

    def getMagicNumber() {
        42
    }
}
src/test/groovy/demo/HelperServiceSpec.groovy
package demo

import grails.testing.services.ServiceUnitTest
import spock.lang.Specification

class HelperServiceSpec extends Specification implements ServiceUnitTest<HelperService> {

    void "test retrieving a property"() {
        expect:
        service.magicNumber == 42
    }
}

Adding the ServiceUnitTest trait to a test causes a new service property to be automatically created for the Service class under test.

4.4 Unit Testing Tag Libraries

The Basics

Tag libraries and GSP pages can be tested with the grails.testing.web.taglib.TagLibUnitTest trait.

grails-app/taglib/demo/SampleTagLib.groovy
package demo

class SampleTagLib {

    static defaultEncodeAs = [taglib:'html']

    static namespace = 'demo'

    def helloWorld = { attrs ->
        out << 'Hello, World!'
    }
}
src/test/groovy/demo/SampleTagLibSpec.groovy
package demo

import grails.testing.web.taglib.TagLibUnitTest
import spock.lang.Specification

class SampleTagLibSpec extends Specification implements TagLibUnitTest<SampleTagLib> {

    void "test simple tag as method"() {
        expect:
        tagLib.helloWorld() == 'Hello, World!'
    }
}

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

To test a tag which accepts parameters, specify the parameter values as named arguments to the method call.

grails-app/taglib/demo/SampleTagLib.groovy
package demo

class SampleTagLib {

    static defaultEncodeAs = [taglib:'html']

    static namespace = 'demo'

    def sayHello = { attrs ->
        out << "Hello, ${attrs.name}!"
    }
}
src/test/groovy/demo/SampleTagLibSpec.groovy
package demo

import grails.testing.web.taglib.TagLibUnitTest
import spock.lang.Specification

class SampleTagLibSpec extends Specification implements TagLibUnitTest<SampleTagLib> {

    void "test tag as method with parameters"() {
        expect:
        tagLib.sayHello(name: 'Robert') == 'Hello, Robert!'
    }
}

Alternatively, tags may be tested with the applyTemplate method which accepts a String parameter that will be evaluated as if it were source code in a GSP.

src/test/groovy/demo/SampleTagLibSpec.groovy
package demo

import grails.testing.web.taglib.TagLibUnitTest
import spock.lang.Specification

class SampleTagLibSpec extends Specification implements TagLibUnitTest<SampleTagLib> {

    void "test tags with applyTemplate"() {
        expect:
        applyTemplate('<demo:helloWorld/>') == 'Hello, World!'
        applyTemplate('<demo:sayHello name="Adrian"/>') == 'Hello, Adrian!'
    }
}

The applyTemplate method accepts an optional second argument which is a Map containing model variables which may be accessed in the GSP snippet that is past as the first argument to applyTemplate as shown below.

grails-app/taglib/demo/SampleTagLib.groovy
package demo

class SampleTagLib {

    static defaultEncodeAs = [taglib:'html']

    static namespace = 'demo'

    def renderSomeNumber = { attrs ->
        int number = attrs.int('value', -1)
        out << "The Number Is ${number}"
    }
}
src/test/groovy/demo/SampleTagLibSpec.groovy
package demo

import grails.testing.web.taglib.TagLibUnitTest
import spock.lang.Specification

class SampleTagLibSpec extends Specification implements TagLibUnitTest<SampleTagLib> {

    void "test a tag that access the model"() {
        expect: 'the value attribute is used in the output'
        applyTemplate('<demo:renderSomeNumber value="${x + y}"/>',
                      [x: 23, y: 19]) == 'The Number Is 42'
    }
}
The String being passed as the first argument to applyTemplate includes a Groovy String expression ("${x + y}") that needs to be evaluated when the GSP snippet is evaluated, not when the code in the test is evaluated. Because of that it is important that the containing String be surrounded by single quotes, not double quotes. '<demo:renderSomeNumber value="${x + y}"/>' works. "<demo:renderSomeNumber value='${x + y}'/>" would not.

Mocking Tag Libraries

In order to test a tag library which invokes tags from another tag library, the second tag library needs to be explicitly mocked by invoking the mockTagLib method.

grails-app/taglib/demo/FirstTagLib.groovy
package demo

class FirstTagLib {
    static defaultEncodeAs = [taglib:'html']

    static namespace = 'one'

    def sayHello = { attrs ->
        out << 'BEFORE '

        // this is invoking a tag from another tag library
        out << two.sayHello()

        out << ' AFTER'
    }
}
grails-app/taglib/demo/SecondTagLib.groovy
package demo

class SecondTagLib {
    static defaultEncodeAs = [taglib:'html']

    static namespace = 'two'

    def sayHello = { attrs ->
        out << 'Hello From SecondTagLib'
    }
}
src/test/groovy/demo/FirstTagLibSpec.groovy
package demo

import grails.testing.web.taglib.TagLibUnitTest
import spock.lang.Specification

class FirstTagLibSpec extends Specification implements TagLibUnitTest<FirstTagLib> {

    void setupSpec() {
        mockTagLib SecondTagLib
    }

    void "test invoking a tag which invokes a tag in another taglib"() {
        expect:
        tagLib.sayHello() == 'BEFORE Hello From SecondTagLib AFTER'
    }
}

4.5 Unit Testing Interceptors

Use the InterceptorUnitTest trait to unit test interceptors.

The interceptor unit test trait provides methods to make testing interceptors easy.

class TestInterceptor {

    TestInterceptor() {
        match(controller: "test")
    }

    boolean before() {
        request.setAttribute('foo', 'Foo is Bar')
        true
    }
}

withRequest

You can use the withRequest method in combination with interceptor.doesMatch() to verify whether or not your interceptor matches the request.

void "Test test interceptor matching"() {
    when:
    withRequest(controller: "test")

    then:
    interceptor.doesMatch()

    when:
    withRequest(controller: "person")

    then:
    !interceptor.doesMatch()
}

withInterceptors

You can use the withInterceptors method to execute code within the context of interceptor execution. This is typically done to call controller actions that rely on behavior from interceptors.

Given this controller action:

grails-app/controllers/demo/TestController.groovy
def renderAttribute() {
    render request.getAttribute('foo')
}

Here is how the action might be tested with withInterceptors:

void "Test controller execution with interceptors"() {
    given:
    def controller = (TestController)mockController(TestController)

    when:
    withInterceptors([controller: "test"]) {
        controller.renderAttribute()
    }

    then:
    response.text == "Foo is Bar"
}

Adding the InterceptorUnitTest trait to a test causes a new interceptor property to be automatically created for the Interceptor class under test.

4.6 Unit Testing Url Mappings

Use the UrlMappingsUnitTest trait to unit test url mappings. Testing url mappings also requires controllers to be mocked to match the mappings to.

Controllers can not be mocked in setupSpec due to the nature of the request being created and reset for each test. The request is not available until the setup method, therefore controllers can not be mocked until then.

All of the methods that check the url mappings come in 2 forms, assert and verify. The assert versions will throw AssertionFailed exceptions, similar to the assert keyword in Groovy. The verify methods will simply return true or false depending on whether the url mapping was found and is valid for the expectations.

The examples assume the following mappings are being used.

package demo

class UrlMappings {

    static mappings = {
        "/foo"(controller: "test", action: "fooGet", method: "GET")
        "/foo"(controller: "test", action: "fooPost", method: "POST")
        "/bar"(controller: "test", action: "bar")

        "/$controller/$action?/$id?(.$format)?"{
            constraints {
                // apply constraints here
            }
        }

        "/"(view:"/index")
        "500"(view:'/error')
        "404"(view:'/notFound')
    }
}

Getting Started

To get started, implement the UrlMappingsUnitTest in your test class and mock controllers you would like to test against.

import grails.testing.web.UrlMappingsUnitTest
import spock.lang.Specification

class UrlMappingsSpec extends Specification implements UrlMappingsUnitTest<UrlMappings> {

    void setup() {
        mockController(TestController)
    }

It is also possible call the mockController method in the given blocks of your feature methods if different controllers need to be tested in different test methods.

Forward Url Mapping Test

Tests whether a URL mapping is forwarded for the given controller class

void "test forward mappings"() {
    expect:
    verifyForwardUrlMapping("/test/renderText", controller: 'test', action: 'renderText')
    verifyForwardUrlMapping("/test/renderView", controller: 'test', action: 'renderView')
    verifyForwardUrlMapping("/test/renderState/123", controller: 'test', action: 'renderState') {
        id = '123'
    }
    verifyForwardUrlMapping("/", view: 'index')
    verifyForwardUrlMapping(500, view: 'error')
    verifyForwardUrlMapping(404, view: 'notFound')

    when: "Using the assert syntax"
    assertForwardUrlMapping("/test/renderText", controller: 'test', action: 'renderText')
    assertForwardUrlMapping("/test/renderView", controller: 'test', action: 'renderView')
    assertForwardUrlMapping("/test/renderState/123", controller: 'test', action: 'renderState') {
        id = 123
    }
    assertForwardUrlMapping("/", view: 'index')
    assertForwardUrlMapping(500, view: 'error')
    assertForwardUrlMapping(404, view: 'notFound')
    
    then:
    noExceptionThrown()
}

Reverse Url Mapping Test

Test whether the given URL is produced when reverse mapping a link to a given controller and action

void "test reverse mappings"() {
    expect:
    verifyReverseUrlMapping("/test/renderText", controller: 'test', action: 'renderText')
    verifyReverseUrlMapping("/test/renderView", controller: 'test', action: 'renderView')
    verifyReverseUrlMapping("/test/renderState/123?foo=bar", controller: 'test', action: 'renderState') {
        id = 123
        foo = 'bar'
    }
    verifyReverseUrlMapping("/", view: 'index')
    
    when: "Using the assert syntax"
    assertReverseUrlMapping("/test/renderText", controller: 'test', action: 'renderText')
    assertReverseUrlMapping("/test/renderView", controller: 'test', action: 'renderView')
    assertReverseUrlMapping("/test/renderState/123?foo=bar", controller: 'test', action: 'renderState') {
        id = 123
        foo = 'bar'
    }
    assertReverseUrlMapping("/", view: 'index')
    
    then:
    noExceptionThrown()
}
Url mappings for HTTP status codes can not be reversed because it doesn’t make sense to "link" to a status code.

Combined

Tests whether a URL mapping is valid for the given URL. This combines the forward and reverse methods.

void "test forward and reverse mappings"() {
    expect:
    verifyUrlMapping("/test/renderText", controller: 'test', action: 'renderText')
    verifyUrlMapping("/test/renderView", controller: 'test', action: 'renderView')
    verifyUrlMapping("/test/renderState/123", controller: 'test', action: 'renderState') {
        id = 123
    }
    verifyUrlMapping("/", view: 'index')

    when: "Using the assert syntax"
    assertUrlMapping("/test/renderText", controller: 'test', action: 'renderText')
    assertUrlMapping("/test/renderView", controller: 'test', action: 'renderView')
    assertUrlMapping("/test/renderState/123", controller: 'test', action: 'renderState') {
        id = 123
    }
    assertUrlMapping("/", view: 'index')
    
    then:
    noExceptionThrown()
}
When calling verifyUrlMapping, then reverse mapping will only be checked if a controller is supplied and the first parameter is not an HTTP status code.

HTTP Methods

When testing HTTP methods on reverse URL mapping it is necessary to specify the HTTP method in the test.

void "test reverse mappings with http methods"() {
    expect:
    !verifyReverseUrlMapping('/foo', controller: 'test', action: 'fooGet')
    verifyReverseUrlMapping('/foo', controller: 'test', action: 'fooGet', method: 'GET')
    verifyReverseUrlMapping('/foo', controller: 'test', action: 'fooPost', method: 'POST')
    verifyReverseUrlMapping('/bar', controller: 'test', action: 'bar')

    when: "Using the assert syntax"
    assertReverseUrlMapping('/foo', controller: 'test', action: 'fooGet', method: 'GET')
    assertReverseUrlMapping('/foo', controller: 'test', action: 'fooPost', method: 'POST')
    assertReverseUrlMapping('/bar', controller: 'test', action: 'bar')

    then:
    noExceptionThrown()
}

When testing HTTP methods on forward URL mapping it is necessary to specify the HTTP method in the request.

void "test forward mappings with http methods"() {
    when: "the http method is GET, /foo should map to TestController.fooGet()"
    request.method = "GET"
    assertForwardUrlMapping('/foo', controller: 'test', action: 'fooGet')

    then:
    noExceptionThrown()

    when: "the http method is POST, /foo should map to TestController.fooPost()"
    request.method = "POST"
    assertForwardUrlMapping('/foo', controller: 'test', action: 'fooPost')

    then:
    noExceptionThrown()
}

When testing HTTP methods on both forward and reverse URL mapping combined it is necessary to specify the HTTP method in both the request and in the test.

void "test forward and reverse mappings with http methods"() {
    when: "the http method is GET, /foo should map to TestController.fooGet()"
    request.method = "GET"
    assertUrlMapping('/foo', controller: 'test', action: 'fooGet', method: 'GET')

    then:
    noExceptionThrown()

    when: "the http method is POST, /foo should map to TestController.fooPost()"
    request.method = "POST"
    assertUrlMapping('/foo', controller: 'test', action: 'fooPost', method: 'POST')

    then:
    noExceptionThrown()
}

Other Helpful Methods

Controller Check

Use the verifyController method to check whether or not the given controller name exists.

void "test controller"() {
    expect:
    verifyController("test")

    when: "Using the assert syntax"
    assertController("test")

    then:
    noExceptionThrown()
}

Action Check

Use the verifyAction method to verify if an action exists for a controller.

void "test action"() {
    expect:
    verifyAction("test", "renderText")

    when: "Using the assert syntax"
    assertAction("test", "renderText")

    then:
    noExceptionThrown()
}

View Check

User the verifyView method to check if a GSP exists for a controller.

void "test view"() {
    expect:
    verifyView("test", "foo")

    when: "Using the assert syntax"
    assertView("test", "foo")

    then:
    noExceptionThrown()
}

4.7 Annotations

@RunOnce

The grails.testing.spock.RunOnce annotation may be applied to any Spock test fixture method that you wish to be executed only once. This is useful when applied in conjunction with a fixture annnotation like @Before as shown below.

src/test/groovy/grails/testing/spock/RunOnceSpec.groovy
package grails.testing.spock

import org.junit.jupiter.api.BeforeEach
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Stepwise

@Stepwise
class RunOnceSpec extends Specification {

    @Shared
    int setupSpecCounter = 0

    @Shared
    int setupCounter = 0

    @Shared
    int onceBeforeCounter = 0

    @Shared
    int anotherOnceBeforeCounter = 0

    void setupSpec() {
        setupSpecCounter++
    }

    void setup() {
        setupCounter++
    }

    @BeforeEach
    @RunOnce
    void someOnceBeforeMethod() {
        onceBeforeCounter++
    }

    @BeforeEach
    @RunOnce
    void someOtherOnceBeforeMethod() {
        anotherOnceBeforeCounter++
    }

    void 'first test'() {
        expect:
        setupSpecCounter == 1
        setupCounter == 1
        onceBeforeCounter == 1
        anotherOnceBeforeCounter == 1
    }

    void 'second test'() {
        expect:
        setupSpecCounter == 1
        setupCounter == 2
        onceBeforeCounter == 1
        anotherOnceBeforeCounter == 1
    }

    void 'third test'() {
        expect:
        setupSpecCounter == 1
        setupCounter == 3
        onceBeforeCounter == 1
        anotherOnceBeforeCounter == 1
    }
}

Applying both the @RunOnce and @Before annotations to a method will yield behavior similar to the behavior associated with Spock’s setupSpec method but an important difference is that setupSpec is run before the test instance is subjected to dependency injection while @Before methods are run after the test instance is subjected to dependency injection. This means that the setupSpec method will not have access to injected variables but methods marked with @Before will have access to injected variables. If a test has some one time setup logic that needs to be executed after dependency injection happens, the RunOnce annotation can help accomplish that.

@OnceBefore

The grails.testing.spock.OnceBefore annotation is a shorthand way of accomplishing the same behavior that would be accomplished by applying both the @RunOnce and @Before annotations to a fixture method.

src/test/groovy/grails/testing/spock/OnceBeforeSpec.groovy
package grails.testing.spock

import org.junit.Before
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Stepwise

@Stepwise
class OnceBeforeSpec extends Specification {

    @Shared
    int setupSpecCounter = 0

    @Shared
    int setupCounter = 0

    @Shared
    int onceBeforeCounter = 0

    @Shared
    int anotherOnceBeforeCounter = 0

    void setupSpec() {
        setupSpecCounter++
    }

    void setup() {
        setupCounter++
    }

    @OnceBefore
    void someOnceBeforeMethod() {
        onceBeforeCounter++
    }

    @OnceBefore
    void someOtherOnceBeforeMethod() {
        anotherOnceBeforeCounter++
    }

    void 'first test'() {
        expect:
        setupSpecCounter == 1
        setupCounter == 1
        onceBeforeCounter == 1
        anotherOnceBeforeCounter == 1
    }

    void 'second test'() {
        expect:
        setupSpecCounter == 1
        setupCounter == 2
        onceBeforeCounter == 1
        anotherOnceBeforeCounter == 1
    }

    void 'third test'() {
        expect:
        setupSpecCounter == 1
        setupCounter == 3
        onceBeforeCounter == 1
        anotherOnceBeforeCounter == 1
    }
}

This is useful in the context of an integration test which wants to reference dependency injected values during setup as shown below.

src/integration-test/groovy/demo/DependencyInjectionSpec.groovy
package demo

import grails.testing.mixin.integration.Integration
import grails.testing.spock.OnceBefore
import spock.lang.Specification

@Integration
class DependencyInjectionSpec extends Specification {

    HelperService helperService

    @OnceBefore
    void init() {
        assert helperService != null
    }

    void 'some test method'() {
        expect:
        helperService != null
    }
}

4.8 Useful Properties

The testing framework provides and initializes a number of properties that are directly accessible unit tests. The javadocs for the various traits describe those properties. Some particular properties of interest:

Properties Available In All Unit Tests

Properties Available In All Web Unit Tests (Controller, Interceptor, Taglib, UrlMappings)

Controller Unit Test Properties

Interceptor Unit Test Properties

Service Unit Test Properties

Tag Library Unit Test Properties

Domain Class Unit Test Properties

5 API Docs

Click here to view the API Documentation.