testCompile "org.grails:grails-gorm-testing-support:3.1.2"
testCompile "org.grails:grails-web-testing-support:3.1.2"
Grails Testing Support
Authors: Jeff Brown,James Kleeh
Version: 3.1.2
Table of Contents
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:
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.
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:
package demo
class DemoController {
def hello() {
render 'Hello, World!'
}
}
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.
package demo
class Person {
String firstName
String lastName
}
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.
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.
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.
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.
package demo
class HelperService {
def getMagicNumber() {
42
}
}
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.
package demo
class SampleTagLib {
static defaultEncodeAs = [taglib:'html']
static namespace = 'demo'
def helloWorld = { attrs ->
out << 'Hello, World!'
}
}
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.
package demo
class SampleTagLib {
static defaultEncodeAs = [taglib:'html']
static namespace = 'demo'
def sayHello = { attrs ->
out << "Hello, ${attrs.name}!"
}
}
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.
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.
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}"
}
}
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.
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'
}
}
package demo
class SecondTagLib {
static defaultEncodeAs = [taglib:'html']
static namespace = 'two'
def sayHello = { attrs ->
out << 'Hello From SecondTagLib'
}
}
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:
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.
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.
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.
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.