(クイックリファレンス)

9 テスト

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith ,
Japanese Translation: T.Yamamoto, Japanese Grails Doc Translating Team
このドキュメントの内容はスナップショットバージョンを元に意訳されているため、一部現行バージョンでは未対応の機能もあります。

Version: 2.1.0.BUILD-SNAPSHOT

9 テスト

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.

Grails 1.3.x and below used the grails.test.GrailsUnitTestCase class hierarchy for testing in a JUnit 3 style. Grails 2.0.x and above deprecates these test harnesses in favour of mixins that can be applied to a range of different kinds of tests (JUnit 3, Junit 4, Spock etc.) without subclassing

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 test/unit/com/acme/app/SimpleControllerTests.groovy. What Grails won't do however is populate the logic inside the test! That is left up to you.

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 both plain text and HTML test reports to the target/test-reports directory, along with the original XML files. 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

Targeting Test Types and/or Phases

In addition to targeting certain tests, you can also target test types and/or phases by using the phase:type syntax.

Grails organises tests by phase and by type. A test phase relates to the state of the Grails application during the tests, and the type relates to the testing mechanism.

Grails comes with support for 4 test phases (unit, integration, functional and other) and JUnit test types for the unit and integration phases. These test types have the same name as the phase.

Testing plugins may provide new test phases or new test types for existing phases. Refer to the plugin documentation.

To execute the JUnit integration tests you can run:

grails test-app integration:integration

Both phase and type are optional. Their absence acts as a wildcard. The following command will run all test types in the unit phase:

grails test-app unit:

The Grails Spock Plugin is one plugin that adds new test types to Grails. It adds a spock test type to the unit, integration and functional phases. To run all spock tests in all phases you would run the following:

grails test-app :spock

To run the all of the spock tests in the functional phase you would run...

grails test-app functional:spock

More than one pattern can be specified...

grails test-app unit:spock integration:spock

Targeting Tests in Types and/or Phases

Test and type/phase targetting can be applied at the same time:

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

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

9.1 Unitテスト

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

Since Grails 2.0, a collection of unit testing mixins is provided by Grails that lets you enhance the behavior of a typical JUnit 3, JUnit 4 or Spock test. The following sections cover the usage of these mixins.

The previous JUnit 3-style GrailsUnitTestCase class hierarchy is still present in Grails for backwards compatibility, but is now deprecated. The previous documentation on the subject can be found in the Grails 1.3.x documentation

You won't normally have to import any of the testing classes because Grails does that for you. But if you find that your IDE for example can't find the classes, here they all are:

  • grails.test.mixin.TestFor
  • grails.test.mixin.TestMixin
  • grails.test.mixin.Mock
  • grails.test.mixin.support.GrailsUnitTestMixin
  • grails.test.mixin.domain.DomainClassUnitTestMixin
  • grails.test.mixin.services.ServiceUnitTestMixin
  • grails.test.mixin.web.ControllerUnitTestMixin
  • grails.test.mixin.web.FiltersUnitTestMixin
  • grails.test.mixin.web.GroovyPageUnitTestMixin
  • grails.test.mixin.web.UrlMappingsUnitTestMixin
  • grails.test.mixin.webflow/WebFlowUnitTestMixin

Note that you're only ever likely to use the first two explicitly. The rest are there for reference.

9.1.1 コントローラUnitテスト

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

@TestFor(SimpleController) class SimpleControllerTests { void testSomething() {

} }

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

void testHello() {
    controller.hello()

assert 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:

// Test class
class SimpleController {
    def index() {
        redirect action: 'hello'
    }
    …
}

void testIndex() {
    controller.index()

assert 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:

void testList() {
    params.sort = "name"
    params.max = 20
    params.offset = 0

controller.list() … }

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

void testSave() {
    request.method = "POST"
    controller.save()
    …
}

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:

void testGetPage() {
    request.method = "POST"
    request.makeAjaxRequest()
    controller.getPage()
    …
}

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:

// Test class
class SimpleController {
    def home() {
        render view: "homePage", model: [title: "Hello World"]
    }
    …
}

void testIndex() {
    controller.home()

assert view == "/simple/homePage" assert model.title == "Hello World" }

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:

void testDisplay() {
    controller.display()
    assert response.text == 'contents of template'
}

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

void testDisplay() {
    views['/simple/_snippet.gsp'] = 'mock contents'
    controller.display()
    assert response.text == 'mock contents'
}

Testing XML and JSON Responses

XML and JSON response are also written directly to the response. Grails' mocking capabilities provide some conveniences for testing XML and JSON response. For example consider the following action:

def renderXml() {
    render(contentType:"text/xml") {
        book(title:"Great")
    }
}

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

void testRenderXml() {
    controller.renderXml()
    assert "<book title='Great'/>" == response.text
    assert "Great" == response.xml.@title.text()
}

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:"text/json") {
        book = "Great"
    }
}

// test
void testRenderJson() {

controller.renderJson()

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

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() {
    def b = new Book(params['book'])

render 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:

void testConsumeBookXml() {
    request.xml = '<book><title>The Shining</title></book>'
    controller.consumeBook()

assert response.text == 'The Shining' }

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

void testConsumeBookXml() {
    request.xml = new Book(title:"The Shining")
    controller.consumeBook()

assert response.text == 'The Shining' }

The same can be done for JSON requests:

void testConsumeBookJson() {
    request.json = new Book(title:"The Shining")
    controller.consumeBook()

assert response.text == 'The Shining' }

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 request.XML.@title
        }
        json {
            render request.JSON.title
        }
    }
}

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

void testConsumeXml() {
    request.xml = '<book title="The Stand" />'

controller.consume()

assert response.text == 'The Stand' }

And, of course, the same can be done for JSON:

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

assert response.text == 'The Stand' }

Testing Spring Beans

When using TestFor only a subset of the Spring beans available to a running Grails application are available. If you wish to make additional beans available you can do so with the defineBeans method of GrailsUnitTestMixin:

class SimpleController {
    SimpleService simpleService
    def hello() {
        render simpleService.sayHello()
    }
}

void testBeanWiring() {
    defineBeans {
        simpleService(SimpleService)
    }

controller.hello()

assert response.text == "Hello World" }

The controller is auto-wired by Spring just like in a running Grails application. Autowiring even occurs if you instantiate subsequent instances of the controller:

void testAutowiringViaNew() {
    defineBeans {
        simpleService(SimpleService)
    }

def controller1 = new SimpleController() def controller2 = new SimpleController()

assert controller1.simpleService != null assert controller2.simpleService != null }

Testing Mime Type Handling

You can test mime type handling and the withFormat method quite simply by setting the response's format attribute:

// controller action
def sayHello() {
    def data = [Hello:"World"]
    withFormat {
        xml { render data as XML }
        html data
    }
}

// test
void testSayHello() {
    response.format = 'xml'
    controller.sayHello()

String expected = '<?xml version="1.0" encoding="UTF-8"?>' + '<map><entry key="Hello">World</entry></map>'

assert expected == response.text }

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:

void testDuplicateFormSubmission() {
    controller.handleForm()
    assert "Bad" == response.text
}

Testing the successful submission requires providing an appropriate SynchronizerToken:

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

void testValidFormSubmission() { def token = SynchronizerToken.store(session) params[SynchronizerToken.KEY] = token.currentToken.toString()

controller.handleForm() assert "Good" == response.text }

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:

controller.handleForm() // first execution
…
response.reset()
…
controller.handleForm() // second execution

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:

void testFileUpload() {
    final file = new GrailsMockMultipartFile("myFile", "foo".bytes)
    request.addFile(file)
    controller.uploadFile()

assert 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:

def handleCommand(SimpleCommand simple) {
    if (simple.hasErrors()) {
        render "Bad"
    }
    else {
        render "Good"
    }
}

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

void testInvalidCommand() {
    def cmd = mockCommandObject(SimpleCommand)
    cmd.name = '' // doesn't allow blank names

cmd.validate() controller.handleCommand(cmd)

assert response.text == 'Bad' }

Testing Calling Tag Libraries

You can test calling tag libraries 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:

void testRenderBasicTemplateWithTags() {
    messageSource.addMessage("foo.bar", request.locale, "Hello World")

controller.showMessage()

assert response.text == "Hello World" }

9.1.2 タグライブラリUnitテスト

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:

@TestFor(SimpleTagLib)
class SimpleTagLibTests {

}

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:

@TestFor(SimpleController)
@Mock(SimpleTagLib)
class GroovyPageUnitTestMixinTests {

}

Testing Custom Tags

The core Grails tags don't need to be enabled during testing, however custom tag libraries do. 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'}" } }

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

@TestFor(SimpleTagLib)
class SimpleTagLibTests {
    void testHelloTag() {
        assert applyTemplate('<s:hello />') == 'Hello World'
        assert applyTemplate('<s:hello name="Fred" />') == 'Hello Fred'
    }
}

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

@grails.test.mixin.TestMixin(GroovyPageUnitTestMixin)
class MultipleTagLibraryTests {

@Test void testMuliple() { mockTagLib(FirstTagLib) mockTagLib(SecondTagLib)

… } }

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

@grails.test.mixin.TestMixin(GroovyPageUnitTestMixin)
class MultipleTagLibraryTests {

@Test void testMuliple() { mockTagLib(FirstTagLib) mockTagLib(SecondTagLib) 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 :

def result = render(template: "/simple/hello")
assert 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.

9.1.3 ドメインUnitテスト

Overview

The mocking support described here is best used when testing non-domain artifacts that use domain classes, to let you focus on testing the artifact without needing a database. But when testing persistence it's best to use integration tests which configure Hibernate and use a database.

Domain class interaction can be tested without involving a database connection using DomainClassUnitTestMixin. This implementation mimics the behavior of GORM against an in-memory ConcurrentHashMap implementation. Note that this has limitations compared to a real GORM implementation. The following features of GORM for Hibernate can only be tested within an integration test:

  • String-based HQL queries
  • composite identifiers
  • dirty checking methods
  • any direct interaction with Hibernate

However 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 something isn't supported then GrailsUnitTestMixin's mockFor method can come in handy to mock the missing pieces. Alternatively you can write an integration test which bootstraps the complete Grails environment at a cost of test execution time.

The Basics

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

import grails.test.mixin.*

@TestFor(SimpleController) @Mock(Simple) class SimpleControllerTests {

}

The example above tests the SimpleController class and mocks the behavior of the Simple domain class as well. For example consider a typical scaffolded save controller action:

class BookController {
    def save() {
        def book = new Book(params)
        if (book.save(flush: true)) {
            flash.message = message(
                    code: 'default.created.message',
                    args: [message(code: 'book.label',
                                   default: 'Book'), book.id])}"
            redirect(action: "show", id: book.id)
        }
        else {
            render(view: "create", model: [bookInstance: book])
        }
    }
}

Tests for this action can be written as follows:

import grails.test.mixin.*

@TestFor(BookController) @Mock(Book) class BookControllerTests {

void testSaveInvalidBook() { controller.save()

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

void testSaveValidBook() { params.title = "The Stand" params.pages = "500"

controller.save()

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

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

@TestFor(BookController)
@Mock([Book, Author])
class BookControllerTests {
   …
}

Alternatively you can also use the DomainClassUnitTestMixin directly with the TestMixin annotation:

import grails.test.mixin.domain.DomainClassUnitTestMixin

@TestFor(BookController) @TestMixin(DomainClassUnitTestMixin) class BookControllerTests { … }

And then call the mockDomain method to mock domains during your test:

void testSave() {
    mockDomain(Author)
    mockDomain(Book)
}

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

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

Testing Constraints

Your constraints contain logic and that logic is highly susceptible to bugs - the kind of bugs that can be tricky to track down (particularly as by default save() doesn't throw an exception when it fails). If your answer is that it's too hard or fiddly, that is no longer an excuse. Enter the mockForConstraintsTests() method.

This method is like a much reduced version of the mockDomain() method that simply adds a validate() method to a given domain class. All you have to do is mock the class, create an instance with populated data, and then call validate(). You can then access the errors property to determine if validation failed. So if all we are doing is mocking the validate() method, why the optional list of test instances? That is so that we can test the unique constraint as you will soon see.

So, suppose we have a simple domain class:

class Book {

String title String author

static constraints = { title blank: false, unique: true author blank: false, minSize: 5 } }

Don't worry about whether the constraints are sensible (they're not!), they are for demonstration only. To test these constraints we can do the following:

@TestFor(Book)
class BookTests {
    void testConstraints() {

def existingBook = new Book( title: "Misery", author: "Stephen King")

mockForConstraintsTests(Book, [existingBook])

// validation should fail if both properties are null def book = new Book()

assert !book.validate() assert "nullable" == book.errors["title"] assert "nullable" == book.errors["author"]

// So let's demonstrate the unique and minSize constraints

book = new Book(title: "Misery", author: "JK") assert !book.validate() assert "unique" == book.errors["title"] assert "minSize" == book.errors["author"]

// Validation should pass! book = new Book(title: "The Shining", author: "Stephen King") assert book.validate() } }

You can probably look at that code and work out what's happening without any further explanation. The one thing we will explain is the way the errors property is used. First, is a real Spring Errors instance, so you can access all the properties and methods you would normally expect. Second, this particular Errors object also has map/property access as shown. Simply specify the name of the field you are interested in and the map/property access will return the name of the constraint that was violated. Note that it is the constraint name, not the message code (as you might expect).

That's it for testing constraints. One final thing we would like to say is that testing the constraints in this way catches a common error: typos in the "constraints" property name! It is currently one of the hardest bugs to track down normally, and yet a unit test for your constraints will highlight the problem straight away.

9.1.4 フィルタUnitテスト

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:

@TestFor(SimpleController)
@Mock(CancellingFilters)
class SimpleControllerTests {

}

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

void testInvocationOfListActionIsFiltered() {
    withFilters(action:"list") {
        controller.list()
    }
    assert 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 a another controller you are testing then you can specify it:

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

9.1.5 URLマッピングUnitテスト

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:

@TestFor(UrlMappings)
class UrlMappingsTests {

}

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 = {
    "/action1"(controller: "simple", action: "action1")
    "/action2"(controller: "simple", action: "action2")
}

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

void testUrlMappings() {

assertForwardUrlMapping("/action1", controller: 'simple', action: "action1")

assertForwardUrlMapping("/action2", controller: 'simple', action: "action2")

shouldFail { assertForwardUrlMapping("/action2", controller: 'simple', action: "action1") } }

Assert Reverse URL Mappings

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.

Simulating Controller Mapping

In addition to the assertions to check the validity of URL mappings you can also simulate mapping to a controller by using your UrlMappings as a mock collaborator and the mapURI method. For example:

@TestFor(SimpleController)
@Mock(UrlMappings)
class SimpleControllerTests {

void testControllerMapping() {

SimpleController controller = mapURI('/simple/list') assert controller != null

def model = controller.list() assert model != null } }

9.1.6 モッキングコラボレータ

Beyond the specific targeted mocking APIs there is also an all-purpose mockFor() method that is available when using the TestFor annotation. The signature of mockFor is:

mockFor(class, loose = false)

This is general-purpose mocking that lets you set up either strict or loose demands on a class.

This method is surprisingly intuitive to use. By default it will create a strict mock control object (one for which the order in which methods are called is important) that you can use to specify demands:

def strictControl = mockFor(MyService)
strictControl.demand.someMethod(0..2) { String arg1, int arg2 -> … }
strictControl.demand.static.aStaticMethod {-> … }

Notice that you can mock static as well as instance methods by using the "static" property. You then specify the name of the method to mock, with an optional range argument. This range determines how many times you expect the method to be called, and if the number of invocations falls outside of that range (either too few or too many) then an assertion error will be thrown. If no range is specified, a default of "1..1" is assumed, i.e. that the method must be called exactly once.

The last part of a demand is a closure representing the implementation of the mock method. The closure arguments must match the number and types of the mocked method, but otherwise you are free to add whatever you want in the body.

As we mentioned before, call mockControl.createMock() to get an actual mock instance of the class that you are mocking. You can call this multiple times to create as many mock instances as you need. And once you have executed the test method, call mockControl.verify() to check that the expected methods were called.

Lastly, the call:

def looseControl = mockFor(MyService, true)

will create a mock control object that has only loose expectations, i.e. the order that methods are invoked does not matter.

9.2 統合テスト

Integration tests differ from unit tests in that you have full access to the Grails environment within the test. Grails uses an in-memory H2 database for integration tests and clears out all the data from the database between tests.

One thing to bear in mind is that logging is enabled for your application classes, but it is different from logging in tests. So if you have something like this:

class MyServiceTests extends GroovyTestCase {
    void testSomething() {
        log.info "Starting tests"
        …
    }
}

the "starting tests" message is logged using a different system than the one used by the application. The log property in the example above is an instance of java.util.logging.Logger (inherited from the base class, not injected by Grails), which doesn't have the same methods as the log property injected into your application artifacts. For example, it doesn't have debug() or trace() methods, and the equivalent of warn() is in fact warning().

Transactions

Integration tests run inside a database transaction by default, which is rolled back at the end of the each test. This means that data saved during a test is not persisted to the database. Add a transactional property to your test class to check transactional behaviour:

class MyServiceTests extends GroovyTestCase {
    static transactional = false

void testMyTransactionalServiceMethod() { … } }

Be sure to remove any persisted data from a non-transactional test, for example in the tearDown method, so these tests don't interfere with standard transactional tests that expect a clean database.

Testing Controllers

To test controllers you first have to understand the Spring Mock Library.

Grails automatically configures each test with a MockHttpServletRequest, MockHttpServletResponse, and MockHttpSession that you can use in your tests. For example consider the following controller:

class FooController {

def text() { render "bar" }

def someRedirect() { redirect(action:"bar") } }

The tests for this would be:

class FooControllerTests extends GroovyTestCase {

void testText() { def fc = new FooController() fc.text() assertEquals "bar", fc.response.contentAsString }

void testSomeRedirect() { def fc = new FooController() fc.someRedirect() assertEquals "/foo/bar", fc.response.redirectedUrl } }

In the above case response is an instance of MockHttpServletResponse which we can use to obtain the generated content with contentAsString (when writing to the response) or the redirected URL. These mocked versions of the Servlet API are completely mutable (unlike the real versions) and hence you can set properties on the request such as the contextPath and so on.

Grails does not invoke interceptors or servlet filters when calling actions during integration testing. You should test interceptors and filters in isolation, using functional testing if necessary.

Testing Controllers with Services

If your controller references a service (or other Spring beans), you have to explicitly initialise the service from your test.

Given a controller using a service:

class FilmStarsController {
    def popularityService

def update() { // do something with popularityService } }

The test for this would be:

class FilmStarsTests extends GroovyTestCase {
    def popularityService

void testInjectedServiceInController () { def fsc = new FilmStarsController() fsc.popularityService = popularityService fsc.update() } }

Testing Controller Command Objects

With command objects you just supply parameters to the request and it will automatically do the command object work for you when you call your action with no parameters:

Given a controller using a command object:

class AuthenticationController {
    def signup(SignupForm form) {
        …
    }
}

You can then test it like this:

def controller = new AuthenticationController()
controller.params.login = "marcpalmer"
controller.params.password = "secret"
controller.params.passwordConfirm = "secret"
controller.signup()

Grails auto-magically sees your call to signup() as a call to the action and populates the command object from the mocked request parameters. During controller testing, the params are mutable with a mocked request supplied by Grails.

Testing Controllers and the render Method

The render method lets you render a custom view at any point within the body of an action. For instance, consider the example below:

def save() {
    def book = Book(params)
    if (book.save()) {
        // handle
    }
    else {
        render(view:"create", model:[book:book])
    }
}

In the above example the result of the model of the action is not available as the return value, but instead is stored within the modelAndView property of the controller. The modelAndView property is an instance of Spring MVC's ModelAndView class and you can use it to the test the result of an action:

def bookController = new BookController()
bookController.save()
def model = bookController.modelAndView.model.book

Simulating Request Data

You can use the Spring MockHttpServletRequest to test an action that requires request data, for example a REST web service. For example consider this action which performs data binding from an incoming request:

def create() {
    [book: new Book(params.book)]
}

To simulate the 'book' parameter as an XML request you could do something like the following:

void testCreateWithXML() {

def controller = new BookController()

controller.request.contentType = 'text/xml' controller.request.content = '''\ <?xml version="1.0" encoding="ISO-8859-1"?> <book> <title>The Stand</title> … </book> '''.stripIndent().getBytes() // note we need the bytes

def model = controller.create() assert model.book assertEquals "The Stand", model.book.title }

The same can be achieved with a JSON request:

void testCreateWithJSON() {

def controller = new BookController()

controller.request.contentType = "text/json" controller.request.content = '{"id":1,"class":"Book","title":"The Stand"}'.getBytes()

def model = controller.create() assert model.book assertEquals "The Stand", model.book.title }

With JSON don't forget the class property to specify the name the target type to bind to. In XML this is implicit within the name of the <book> node, but this property is required as part of the JSON packet.

For more information on the subject of REST web services see the section on REST.

Testing Web Flows

Testing Web Flows requires a special test harness called grails.test.WebFlowTestCase which subclasses Spring Web Flow's AbstractFlowExecutionTests class.

Subclasses of WebFlowTestCase must be integration tests

For example given this simple flow:

class ExampleController {

def exampleFlow() { start { on("go") { flow.hello = "world" }.to "next" } next { on("back").to "start" on("go").to "subber" } subber { subflow(action: "sub") on("end").to("end") } end() }

def subFlow() { subSubflowState { subflow(controller: "other", action: "otherSub") on("next").to("next") } … } }

You need to tell the test harness what to use for the "flow definition". This is done via overriding the abstract getFlow method:

import grails.test.WebFlowTestCase

class ExampleFlowTests extends WebFlowTestCase { def getFlow() { new ExampleController().exampleFlow } … }

You can specify the flow id by overriding the getFlowId method, otherwise the default is test:

import grails.test.WebFlowTestCase

class ExampleFlowTests extends WebFlowTestCase { String getFlowId() { "example" } … }

If the flow under test calls any subflows, these (or mocks) must be registered before the calling the flow:

protected void setUp() {
    super.setUp()

registerFlow("other/otherSub") { // register a simplified mock start { on("next").to("end") } end() }

// register the original subflow registerFlow("example/sub", new ExampleController().subFlow) }

Then you kick off the flow with the startFlow method:

void testExampleFlow() {
    def viewSelection = startFlow()
    …
}

Use the signalEvent method to trigger an event:

void testExampleFlow() {
    …
    signalEvent("go")
    assert "next" == flowExecution.activeSession.state.id
    assert "world" == flowScope.hello
}

Here we have signaled to the flow to execute the event "go" which causes a transition to the "next" state. In the example a transition action placed a hello variable into the flow scope.

Testing Tag Libraries

Testing tag libraries is simple because when a tag is invoked as a method it returns its result as a string (technically a StreamCharBuffer but this class implements all of the methods of String). So for example if you have a tag library like this:

class FooTagLib {

def bar = { attrs, body -> out << "<p>Hello World!</p>" }

def bodyTag = { attrs, body -> out << "<${attrs.name}>" out << body() out << "</${attrs.name}>" } }

The tests would look like:

class FooTagLibTests extends GroovyTestCase {

void testBarTag() { assertEquals "<p>Hello World!</p>", new FooTagLib().bar(null, null).toString() }

void testBodyTag() { assertEquals "<p>Hello World!</p>", new FooTagLib().bodyTag(name: "p") { "Hello World!" }.toString() } }

Notice that for the second example, testBodyTag, we pass a block that returns the body of the tag. This is convenient to representing the body as a String.

Testing Tag Libraries with GroovyPagesTestCase

In addition to doing simple testing of tag libraries like in the above examples, you can also use the grails.test.GroovyPagesTestCase class to test tag libraries with integration tests.

The GroovyPagesTestCase class is a subclass of the standard GroovyTestCase class and adds utility methods for testing the output of GSP rendering.

GroovyPagesTestCase can only be used in an integration test.

For example, consider this date formatting tag library:

import java.text.SimpleDateFormat

class FormatTagLib { def dateFormat = { attrs, body -> out << new SimpleDateFormat(attrs.format) << attrs.date } }

This can be easily tested as follows:

class FormatTagLibTests extends GroovyPagesTestCase {
    void testDateFormat() {
        def template =
                '<g:dateFormat format="dd-MM-yyyy" date="${myDate}" />'

def testDate = … // create the date assertOutputEquals('01-01-2008', template, [myDate:testDate]) } }

You can also obtain the result of a GSP using the applyTemplate method of the GroovyPagesTestCase class:

class FormatTagLibTests extends GroovyPagesTestCase {
    void testDateFormat() {
        def template =
                '<g:dateFormat format="dd-MM-yyyy" date="${myDate}" />'

def testDate = … // create the date def result = applyTemplate(template, [myDate:testDate])

assertEquals '01-01-2008', result } }

Testing Domain Classes

Testing domain classes is typically a simple matter of using the GORM API, but there are a few things to be aware of. Firstly, when testing queries you often need to "flush" to ensure the correct state has been persisted to the database. For example take the following example:

void testQuery() {
    def books = [
            new Book(title: "The Stand"),
            new Book(title: "The Shining")]
    books*.save()

assertEquals 2, Book.list().size() }

This test will fail because calling save does not actually persist the Book instances when called. Calling save only indicates to Hibernate that at some point in the future these instances should be persisted. To commit changes immediately you "flush" them:

void testQuery() {
    def books = [
            new Book(title: "The Stand"),
            new Book(title: "The Shining")]
    books*.save(flush: true)

assertEquals 2, Book.list().size() }

In this case since we're passing the argument flush with a value of true the updates will be persisted immediately and hence will be available to the query later on.

9.3 機能テスト

Functional tests involve making HTTP requests against the running application and verifying the resultant behaviour. Grails does not ship with any support for writing functional tests directly, but there are several plugins available for this.

Consult the documentation for each plugin for its capabilities.

Common Options

There are options that are common to all plugins that control how the Grails application is launched, if at all.

inline

The -inline option specifies that the grails application should be started inline (i.e. like run-app).

This option is implicitly set unless the baseUrl or war options are set

war

The -war option specifies that the grails application should be packaged as a war and started. This is useful as it tests your application in a production-like state, but it has a longer startup time than the -inline option. It also runs the war in a forked JVM, meaning that you cannot access any internal application objects.

grails test-app functional: -war

Note that the same build/config options for the run-war command apply to functional testing against the WAR.

https

The -https option results in the application being able to receive https requests as well as http requests. It is compatible with both the -inline and -war options.

grails test-app functional: -https

Note that this does not change the test base url to be https, it will still be http unless the -httpsBaseUrl option is also given.

httpsBaseUrl

The -httpsBaseUrl causes the implicit base url to be used for tests to be a https url.

grails test-app functional: -httpsBaseUrl

This option is ignored if the -baseUrl option is specified.

baseUrl

The baseUrl option allows the base url for tests to be specified.

grails test-app functional: -baseUrl=http://mycompany.com/grailsapp

This option will prevent the local grails application being started unless -inline or -war are given as well. To use a custom base url but still test against the local Grails application you must specify one of either the -inline or -war options.