(Quick Reference)

15 Testing

Version: 5.2.4

15 Testing

Automated testing is a key part of Grails. Hence, Grails provides many ways to making testing easier from low level unit testing to high level functional tests. This section details the different capabilities that Grails offers for testing.

The first thing to be aware of is that all of the create-* and generate-\* commands create unit or integration tests automatically. For example if you run the create-controller command as follows:

grails create-controller com.acme.app.simple

Grails will create a controller at grails-app/controllers/com/acme/app/SimpleController.groovy, and also a unit test at src/test/groovy/com/acme/app/SimpleControllerSpec.groovy. What Grails won’t do however is populate the logic inside the test! That is left up to you.

The default class name suffix is Tests but as of Grails 1.2.2, the suffix of Test is also supported.

Running Tests

Tests are run with the test-app command:

grails test-app

The command will produce output such as:

-------------------------------------------------------
Running Unit Tests...
Running test FooTests...FAILURE
Unit Tests Completed in 464ms ...
-------------------------------------------------------

Tests failed: 0 errors, 1 failures

whilst showing the reason for each test failure.

You can force a clean before running tests by passing -clean to the test-app command.

Grails writes HTML test reports to the build/reports/tests directory and JUnit XML test reports to the build/test-results directory. The HTML reports are generally the best ones to look at.

Using Grails' interactive mode confers some distinct advantages when executing tests. First, the tests will execute significantly faster on the second and subsequent runs. Second, a shortcut is available to open the HTML reports in your browser:

open test-report

You can also run your unit tests from within most IDEs.

Targeting Tests

You can selectively target the test(s) to be run in different ways. To run all tests for a controller named SimpleController you would run:

grails test-app SimpleController

This will run any tests for the class named SimpleController. Wildcards can be used…​

grails test-app *Controller

This will test all classes ending in Controller. Package names can optionally be specified…​

grails test-app some.org.*Controller

or to run all tests in a package…​

grails test-app some.org.*

or to run all tests in a package including subpackages…​

grails test-app some.org.**.*

You can also target particular test methods…​

grails test-app SimpleController.testLogin

This will run the testLogin test in the SimpleController tests. You can specify as many patterns in combination as you like…​

grails test-app some.org.* SimpleController.testLogin BookController
In Grails 2.x, adding -rerun as an argument would only run those tests which failed in the previous test-app run. This argument is no longer supported.
In Grails 3.x, you might need to specify the package name before the class name, as well as append "Spec" to the end. For example, if you want to run the test for the ProductController, you should use grails test-app *.ProductControllerSpec. Note that the star can be used if you don’t want to type the whole package hierarchy.

Debugging

In order to debug your tests via a remote debugger, you can add --debug-jvm after grails in any commands, like so:

grails --debug-jvm test-app

This will open the default Java remote debugging port, 5005, for you to attach a remote debugger from your editor / IDE of choice.

This differs from Grails 2.3 and previous, where the grails-debug command existed.

Targeting Test Phases

In addition to targeting certain tests, you can also target test phases. By default Grails has two testing phases unit and integration.

Grails 2.x uses phase:type syntax. In Grails 3.0 it was removed, because it made no sense in Gradle context.

To execute unit tests you can run:

grails test-app -unit

To run integration tests you would run…​

grails test-app -integration

Targeting Tests When Using Phases

Test and phase targeting can be applied at the same time:

grails test-app some.org.**.* -unit

This would run all tests in the unit phase that are in the package some.org or a subpackage.

15.1 Unit Testing

Unit testing are tests at the "unit" level. In other words you are testing individual methods or blocks of code without consideration for surrounding infrastructure. Unit tests are typically run without the presence of physical resources that involve I/O such as databases, socket connections or files. This is to ensure they run as quick as possible since quick feedback is important.

Since Grails 3.3, the Grails Testing Support Framework is used for all unit tests. This support provides a set of traits. An example hello world test can be seen below:

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

class HelloControllerTests extends Specification implements ControllerUnitTest<HelloController> {

    void "Test message action"() {
        when:"The message action is invoked"
        controller.message()

        then:"Hello is returned"
        response.text == 'Hello'
    }
}

For more information on writing tests with Grails Testing Support see the dedicated documentation.

Versions of Grails below 3.2 used the Grails Test Mixin Framework which was based on the @TestMixin AST transformation. This library has been superceded by the simpler and more IDE friendly trait based implementation.

15.2 Integration Testing

Integration tests differ from unit tests in that you have full access to the Grails environment within the test. You can create an integration test using the create-integration-test command:

$ grails create-integration-test Example

The above command will create a new integration test at the location src/integration-test/groovy/<PACKAGE>/ExampleSpec.groovy.

Grails uses the test environment for integration tests and loads the application prior to the first test run. All tests use the same application state.

Transactions

Integration test methods run inside their own database transaction by default, which is rolled back at the end of each test method. This means that data saved during a test is not persisted to the database (which is shared across all tests). The default generated integration test template includes the Rollback annotation:

import grails.testing.mixin.integration.Integration
import grails.gorm.transactions.*
import spock.lang.*

@Integration
@Rollback
class ExampleSpec extends Specification {

    ...

    void "test something"() {
        expect:"fix me"
            true == false
    }
}

The Rollback annotation ensures that each test method runs in a transaction that is rolled back. Generally this is desirable because you do not want your tests depending on order or application state.

In Grails 3.0 tests rely on grails.gorm.transactions.Rollback annotation to bind the session in integration tests. Though each test method transaction is rolled back, the setup() method uses a separate transaction that is not rolled back. Data will persist to the database and will need to be cleaned up manually if setup() sets up data and persists them as shown in the below sample:

import grails.testing.mixin.integration.Integration
import grails.gorm.transactions.*
import spock.lang.*

@Integration
@Rollback
class BookSpec extends Specification {

    void setup() {
        // Below line would persist and not roll back
        new Book(name: 'Grails in Action').save(flush: true)
    }

    void "test something"() {
        expect:
        Book.count() == 1
    }
}

To preload the database and automatically roll back setup logic, any persistence operations need to be called from the test method itself so that they can run within the test method’s rolled back transaction. Similar to usage of the setupData() method shown below which creates a record in database and after running other test will be rolled back:

import grails.testing.mixin.integration.Integration
import grails.gorm.transactions.*
import spock.lang.*

@Integration
@Rollback
class BookSpec extends Specification {

    void setupData() {
        // Below line would roll back
        new Book(name: 'Grails in Action').save(flush: true)
    }

    void "test something"() {
        given:
        setupData()

        expect:
        Book.count() == 1
    }
}

Using Spring’s Rollback annotation

Another transactional approach could be to use Spring’s @Rollback instead.

import grails.testing.mixin.integration.Integration
import org.springframework.test.annotation.Rollback
import spock.lang.*

@Integration
@Rollback
class BookSpec extends Specification {

    void setup() {
        new Book(name: 'Grails in Action').save(flush: true)
    }

    void "test something"() {
        expect:
        Book.count() == 1
    }
}
It isn’t possible to make grails.gorm.transactions.Rollback behave the same way as Spring’s Rollback annotation because grails.gorm.transactions.Rollback transforms the byte code of the class, eliminating the need for a proxy (which Spring’s version requires). This has the downside that you cannot implement it differently for different cases (as Spring does for testing).

DirtiesContext

If you do have a series of tests that will share state you can remove the Rollback and the last test in the suite should feature the DirtiesContext annotation which will shutdown the environment and restart it fresh (note that this will have an impact on test run times).

Autowiring

To obtain a reference to a bean you can use the Autowired annotation. For example:

...
import org.springframework.beans.factory.annotation.*

@Integration
@Rollback
class ExampleServiceSpec extends Specification {

    @Autowired
    ExampleService exampleService
    ...

    void "Test example service"() {
        expect:
            exampleService.countExamples() == 0
    }
}

Testing Controllers

To integration test controllers it is recommended you use create-functional-test command to create a Geb functional test. See the following section on functional testing for more information.

15.3 Functional Testing

Functional tests involve making HTTP requests against the running application and verifying the resultant behaviour. This is useful for end-to-end testing scenarios, such as making REST calls against a JSON API.

Grails by default ships with support for writing functional tests using the Geb framework. To create a functional test you can use the create-functional-test command which will create a new functional test:

$ grails create-functional-test MyFunctional

The above command will create a new Spock spec called MyFunctionalSpec.groovy in the src/integration-test/groovy directory. The test is annotated with the Integration annotation to indicate it is an integration test and extends the GebSpec super class:

@Integration
class HomeSpec extends GebSpec {

    def setup() {
    }

    def cleanup() {
    }

    void "Test the home page renders correctly"() {
        when:"The home page is visited"
            go '/'

        then:"The title is correct"
            $('title').text() == "Welcome to Grails"
    }
}

When the test is run the application container will be loaded up in the background and you can send requests to the running application using the Geb API.

Note that the application is only loaded once for the entire test run, so functional tests share the state of the application across the whole suite.

In addition the application is loaded in the JVM as the test, this means that the test has full access to the application state and can interact directly with data services such as GORM to setup and cleanup test data.

The Integration annotation supports an optional applicationClass attribute which may be used to specify the application class to use for the functional test. The class must extend GrailsAutoConfiguration.

@Integration(applicationClass=com.demo.Application)
class HomeSpec extends GebSpec {

    // ...

}

If the applicationClass is not specified then the test runtime environment will attempt to locate the application class dynamically which can be problematic in multiproject builds where multiple application classes may be present.

When running the server port by default will be randomly assigned. The Integration annotation adds a property of serverPort to the test class that you can use if you want to know what port the application is running on this isn’t needed if you are extending the GebSpec as shown above but can be useful information.

If you want to run the tests on a fixed port (defined by the server.port configuration property), you need to manually annotate your test with @SpringBootTest:

import grails.testing.mixin.integration.Integration
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification

@Integration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class MySpec extends Specification {

    // ...

}