12 テスト - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: 2.3.0
Translated by: T.Yamamoto, Japanese Grails Doc Translating Team. Special thanks to NTT Software.
【注意】このドキュメントの内容はスナップショットバージョンを元に*意訳*されているため、一部現行バージョンでは未対応の機能もあります。
Table of Contents
12 テスト
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
Grails 1.3.X以下のバージョンでは、JUnit 3スタイルのテストのためにgrails.test.GrailsUnitTestCase
のクラスを継承するようになっていましたが、Grails 2.0.X 以上のバージョンではサブクラスを作ることなく異なる種類のテスト(Junit 3, Junit4, Spock など)を実施可能なMixinsベースのテストを推奨しています。
create-*
and generate-*
commands create unit
or integration
tests automatically. For example if you run the create-controller command as follows:create-*
やgenerate-*
のコマンドはunit
もしくはintegration
テストを自動的に生成します。例えば、create-controllerコマンドを以下のように実行したとします。:grails create-controller com.acme.app.simple
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.grails-app/controllers/com/acme/app/SimpleController.groovy
にコントローラを、test/unit/com/acme/app/SimpleControllerTests.groovy
にユニットテストを作ります。しかしながら、Grailsはテストの中のロジックは作成しません!それはあなたに任されています。The default class name suffix isTests
but as of Grails 1.2.2, the suffix ofTest
is also supported.
デフォルトのクラス名の最後にはTests
を付けます。しかし、Grails 1.2.2からはTest
のサフィックスもサポートしています。
テストの実行
grails test-app
------------------------------------------------------- Running Unit Tests… Running test FooTests...FAILURE Unit Tests Completed in 464ms … -------------------------------------------------------Tests failed: 0 errors, 1 failures
You can force a clean before running tests by passing-clean
to thetest-app
command.
test-app
コマンドに-clean
を付与することにより、テスト実行前に強制的にテスト実行環境をクリーンにすることができます。
target/test-reports
directory, along with the original XML files. The HTML reports are generally the best ones to look at.target/test-reports
ディレクトリにオリジナルのXMLファイルとともにプレインテキストとHTMLのテストレポートの両方を出力します。最も見栄えがよいのはHTMLレポートです。open test-report
unit
テストを実行できます。テストを指定する
SimpleController
you would run:SimpleController
と名付けられたコントローラの全てのテストを実行したい場合には以下のように指定します。:grails test-app SimpleController
SimpleController
. Wildcards can be used...SimpleController
という名前に対応する全てのテストを実行します。ワイルドカードを利用することも可能です。:grails test-app *Controller
Controller
. Package names can optionally be specified...Controller
でクラス名が終わる全てのクラスのテストを実行します。パッケージ名も指定可能です。grails test-app some.org.*Controller
grails test-app some.org.*
grails test-app some.org.**.*
grails test-app SimpleController.testLogin
testLogin
test in the SimpleController
tests. You can specify as many patterns in combination as you like...SimpleController
テストの中にあるtestLogin
テストを実行します。いくつかのパターンを組合せて好きなように指定できます。grails test-app some.org.* SimpleController.testLogin BookController
テスト種別とテストフェーズの条件によるテストの指定
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
andother
) and JUnit test types for theunit
andintegration
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.
Grailsはテストフェーズとテスト種別によりテストを体系化しています。テストフェーズはGrailsアプリケーションにおけるどの状態のテストを行っているかを表しており、テスト種別はテストを行う機構に関連しています。Grailsは4つのテストフェーズ(
unit
,integration
,functional
そしてother
)とunit
とintegration
の各テストフェーズに対応したJUnitのテスト種別をサポートしています。これらのテスト種別はそのテストフェーズと同じ名前になっています。テストプラグインにより既存のテストフェーズに新しいテストフェーズもしくはテスト種別を追加することができます。詳しくはプラグインのドキュメントを参照してください。
integration
tests you can run:integration
テストを実行するには以下のように実行します:grails test-app integration:integration
phase
and type
are optional. Their absence acts as a wildcard. The following command will run all test types in the unit
phase:テストフェーズ
とテスト種別
の指定は任意です。もし指定をしなかった場合には全てが該当します。以下のコマンドではunit
テストフェーズの全てのテスト種別のテストを実行します。:grails test-app unit:
spock
test type to the unit
, integration
and functional
phases. To run all spock tests in all phases you would run the following:spock
テスト種別がunit
、integration
そしてfunctional
テストフェーズで使えるようになります。全てのテストフェーズで全てのspock
テストを実行するには次のように実行します。:grails test-app :spock
functional
phase you would run...functional
フェーズの全てのspock
テストを実行するには次のように実行します。grails test-app functional:spock
grails test-app unit:spock integration:spock
テスト種別とテストフェーズを用いたテストの指定
grails test-app integration: unit: some.org.**.*
integration
and unit
phases that are in the package some.org
or a subpackage.some.org
のパッケージかサブパッケージの中でintegration
とunit
フェーズの全てのテストが実行されるでしょう。
12.1 ユニットテスト
Unitテストは"単体"レベルのテストです。具体的には、環境周りを考慮せずに個々のメソッドやブロック単位で行うテストのことです。Unitテストは基本的にデータベース、ネットワーク接続、ファイルなどのI/Oを必要とする物理リソースを必要としません。素早いフィードバックを得られるように、Unitテストの実行時間はできる限り短くしてください。
The Test Mixins
テストMixin
Grails 2.0以降、MixinのUnitテストライブラリがGrailsによって提供されており、代表的なJUnit 3、JUnit 4、Spockを使用したテストの実行をサポートします。以下の節では、Mixinの使い方について記載しています。
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
以前のGrailsUnitTestCase
クラスを継承するJUnit 3スタイルは、下位バージョンとの互換性のためにGrailsにまだ存在しています。しかし今は非推奨です。過去のドキュメントはGrails 1.3.x ドキュメンテーションで見つけることができます。
Grailsは自動でテストクラスをインポートするため、全てのテストクラスをインポートしなくても問題ありません。しかし、IDEでテストクラスを探せないことがあるかもしれないので、下記にそれらすべてのテストクラスを列挙します。
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
Test Mixin Basics
テストMixinの基本
TestFor
annotation in combination with the Mock
annotation for mocking collaborators. For example, to test a controller and associated domains you would define the following:
ほとんどのテストは、モックを使用するためMock
アノテーションとTestFor
アノテーションを組み合わせて使用します。例えば、コントローラーとドメインをテストするためには次のように定義します。
@TestFor(BookController) @Mock([Book, Author, BookService])
TestFor
annotation defines the class under test and will automatically create a field for the type of class under test. For example in the above case a "controller" field will be present, however if TestFor
was defined for a service a "service" field would be created and so on.
TestFor
アノテーションは、テスト対象のクラスを定義することで自動的にクラスタイプのフィールドを生成します。例えば、上記の場合、"controller"フィールドを生成します。また、もしTestFor
にサービスが定義されている場合、"service"フィールドが生成されます。
Mock
annotation creates mock version of any collaborators. There is an in-memory implementation of GORM that will simulate most interactions with the GORM API. For those interactions that are not automatically mocked you can use the built in support for defining mocks and stubs programmatically. For example:
Mock
アノテーションは複数の種類のモックを生成します。ほとんどのGORM APIの振る舞いをシミュレートするインメモリのGORM実装があります。自動でモック化されない振る舞いに対しては、ビルトインされているモック、スタブの作成機能が使用できます。例えば、次の通りです。
void testSearch() { def control = mockFor(SearchService) control.demand.searchWeb { String q -> ['mock results'] } control.demand.static.logResults { List results -> } controller.searchService = control.createMock() controller.search()assert controller.response.text.contains "Found 1 results" }
12.1.1 コントローラのユニットテスト
The Basics
基本
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:
コントローラUnitテストには、grails.test.mixin.TestFor
アノテーションを使用します。このようにTestFor
を使用することで、grails.test.mixin.web.ControllerUnitTestMixin
とその関連APIが有効になります。
import grails.test.mixin.TestFor@TestFor(SimpleController) class SimpleControllerTests { void testSomething() {
} }
TestFor
annotation to a controller causes a new controller
field to be automatically created for the controller under test.
TestFor
アノテーションにコントローラを追加することにより、controller
フィールドにテスト対象のコントローラが自動的に生成されます。
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
TestFor
アノテーションは"test"から始まるパブリックなメソッドに自動的にJUnit4のTestアノテーションを付与します。もしテストメソッドが"test"で始まらない場合は
Testアノテーションを手動で付与してください。
最もシンプルなHello World形式のテストは次の通りです。
// Test class
class SimpleController {
def hello() {
render "hello"
}
}
void testHello() { controller.hello()assert response.text == 'hello' }
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.
response
オブジェクトは、GrailsMockHttpServletResponse
(org.codehaus.groovy.grails.plugins.testing
パッケージに含まれている)のインスタンスです。SpringのMockHttpServletResponse
クラスを継承しており、レスポンスの状態を調べる便利なメソッドを定義しています。
redirectedUrl
property:
例えば、リダイレクトをテストするにはredirectedUrl
プロパティを使用できます。
// Test class class SimpleController { def index() { redirect action: 'hello' } … }
void testIndex() { controller.index()assert response.redirectedUrl == '/simple/hello' }
params
variable:
ほとんどのアクションはリクエストに設定されたパラメータを使用します。例えば、'sort'、'max'、'offset'といったパラメータが一般的です。これらのパラメータをテストで利用するにはparams
変数に値を代入するだけで簡単に設定できます。
void testList() { params.sort = "name" params.max = 20 params.offset = 0controller.list() … }
method
property of the mock request:
リクエストのmethod
プロパティにリクエストタイプを設定することにより、リクエストタイプ毎のコントローラのアクションを確認できます。
void testSave() {
request.method = "POST"
controller.save()
…
}
リクエストタイプに応じてアクションが違った挙動をする場合、これは非常に重要です。さらに、次のようにAjaxとしてリクエストを作ることもできます。
void testGetPage() {
request.method = "POST"
request.makeAjaxRequest()
controller.getPage()
…
}
xhr
property on the request.
テスト対象コードのリクエストでxhr
プロパティを使用してたとしても、これを実行するだけです。
Testing View Rendering
ビューのレンダリングテスト
modelAndView
property (an instance of org.springframework.web.servlet.ModelAndView
) or you can use the view
and model
properties provided by the mixin:
コントローラのmodelAndView
プロパティ(org.springframework.web.servlet.ModelAndView
のインスタンス)、または、Mixinで提供されているview
とmodel
プロパティを使用することにより、ビューのレンダリングをテストすることができます。
// 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" }
viewの文字列は絶対パスであることに注意してください。これは、'/'から始まり、動作するコントローラの名前から決まるディレクトリ名などがパスとして含まれます。
Testing Template Rendering
テンプレートレンダリングテスト
ModelAndView
hence it requires a different approach to testing.
テンプレートのレンダリングはModelAndView
を返すのではなく、レスポンスにテンプレートの内容を直接書き込みます。そのため、ビューのレンダリングとは異なるテストのアプローチが必要になります。
次のコントローラアクションを見てください。
class SimpleController {
def display() {
render template:"snippet"
}
}
grails-app/views/simple/_snippet.gsp
. You can test this as follows:
この例では、コントローラはgrails-app/views/simple/_snippet.gsp
にあるテンプレートを探します。そして、次のようにテストをします。
void testDisplay() { controller.display() assert response.text == 'contents of template' }
テンプレートをレンダリングしたくない場合にも、テストでは実際にテンプレートがレンダリングされます。このような場合はGroovy Pagesのモックが使用できます。
void testDisplay() { views['/simple/_snippet.gsp'] = 'mock contents' controller.display() assert response.text == 'mock contents' }
Testing Actions Which Return A Map
マップを返すアクションをテスト
java.util.Map
that Map
may be inspected directly to assert that it contains the expected data:
コントローラアクションがjava.util.Map
を返す場合は、Map
に期待したデータが含まれているか直接検証できます。
class SimpleController { def showBookDetails() { [title: 'The Nature Of Necessity', author: 'Alvin Plantinga'] } }
import grails.test.mixin.*@TestFor(SimpleController) class SimpleControllerTests {
void testShowBookDetails() { def model = controller.showBookDetails()
assert model.author == 'Alvin Plantinga' } }
Testing XML and JSON Responses
XMLとJSONレスポンスのテスト
XMLとJSONのレスポンスもまた、直接レスポンスに書き込まれます。Grailsのモック機構は、XMLとJSONのレスポンスをテストするために便利な機能をいくつか提供しています。
def renderXml() { render(contentType:"text/xml") { book(title:"Great") } }
xml
property of the response:
次の例は、レスポンスのxml
プロパティを使ってテストしています。
void testRenderXml() { controller.renderXml() assert "<book title='Great'/>" == response.text assert "Great" == response.xml.@title.text() }
xml
property is a parsed result from Groovy's XmlSlurper class which is very convenient for parsing XML.
xml
プロパティは、XMLをパースするためにとても便利なGroovyのXmlSlurper でパースされた結果です。
json
property:
json
プロパティを使うだけで、JSONレスポンスのテストもかなり似ています。
// controller action def renderJson() { render(contentType:"application/json") { book = "Great" } }
// test void testRenderJson() {controller.renderJson()
assert '{"book":"Great"}' == response.text assert "Great" == response.json.book }
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.
json
プロパティは、JSONレスポンスをパースするために便利なorg.codehaus.groovy.grails.web.json.JSONElement
のインスタンスでマップライクな構造をもっています。
Testing XML and JSON Requests
XMLとJSONリクエストのテスト
Grailsは、入力されるXMLとJSONパケットを自動的にパースするためのいろいろな便利な方法を提供しています。例えば、入力されるJSONかXMLリクエストをGrailsのデータバインディングを使ってバインドできます。
def consumeBook() { def b = new Book(params['book'])render b.title }
xml
or json
properties. For example the above action can be tested by specifying a String containing the XML:
次のテストでは、xml
やjson
プロパティを用いてXMLやJSONリクエストを設定しています。例えば、上記のアクションはXMLの文字列を設定することによってテストができます。
void testConsumeBookXml() { request.xml = '<book><title>The Shining</title></book>' controller.consumeBook()assert response.text == 'The Shining' }
もしくは、ドメインインスタンスを設定することで、適切なXMLリクエストに自動変換してテストをします。
void testConsumeBookXml() { request.xml = new Book(title:"The Shining") controller.consumeBook()assert response.text == 'The Shining' }
JSONリクエストでも同じようにテストできます。
void testConsumeBookJson() { request.json = new Book(title:"The Shining") controller.consumeBook()assert response.text == 'The Shining' }
もし、Grailsのデータバインディングを使用したくない場合は、入力されるXMLまたはJSONを手動でパースする必要がありますが、このような場合も同じようにテストできます。次のコントローラアクションを見てください。
def consume() { request.withFormat { xml { render request.XML.@title } json { render request.JSON.title } } }
XMLリクエストをテストするためには、XMLの文字列を設定します。
void testConsumeXml() { request.xml = '<book title="The Stand" />'controller.consume()
assert response.text == 'The Stand' }
もちろん、同じことがJSONでもできます。
void testConsumeJson() { request.json = '{title:"The Stand"}' controller.consume()assert response.text == 'The Stand' }
Testing Spring Beans
Springビーンのテスト
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
:
TestFor
を使用した場合は、Grailsアプリケーションを実行するのに必要なSpringビーンのサブセットだけが利用できます。もし、利用できるビーンを追加したい場合は、GrailsUnitTestMixin
のdefineBeans
メソッドを使うことにより、ビーンを追加できます。
class SimpleController { SimpleService simpleService def hello() { render simpleService.sayHello() } }
void testBeanWiring() { defineBeans { simpleService(SimpleService) }controller.hello()
assert response.text == "Hello World" }
コントローラは実行中のGrailsアプリケーションのように、Springによって自動的にビーンが注入されます。もしコントローラを後でインスタンス化した場合も自動的に注入されます。
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
MIME Typeの制御のテスト
withFormat
method quite simply by setting the response's format
attribute:
MIME Typeの制御とwithFormat
メソッドは、レスポンスのformat
属性を設定することにより簡単にテストができます。
// 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
フォームの2重送信テスト
フォームの2重送信のテストは少し複雑になります。例えば、次のようなフォームを処理するアクションがあるとします。
def handleForm() { withForm { render "Good" }.invalidToken { render "Bad" } }
ここでフォームの送信が成功する場合と、2重送信となる場合のロジックを検証したいとします。2重送信となる場合のテストは、単にコントローラをすることで簡単に実行できます。
void testDuplicateFormSubmission() {
controller.handleForm()
assert "Bad" == response.text
}
SynchronizerToken
:
フォームの送信が成功する場合のテストは適切なSynchronizerToken
が必要となります。
import org.codehaus.groovy.grails.web.servlet.mvc.SynchronizerToken ...void testValidFormSubmission() { def tokenHolder = SynchronizerTokensHolder.store(session)
params[SynchronizerTokensHolder.TOKEN_URI] = '/controller/handleForm' params[SynchronizerTokensHolder.TOKEN_KEY] = tokenHolder.generateToken(params[SynchronizerTokensHolder.TOKEN_URI])
controller.handleForm() assert "Good" == response.text }
もし、有効/無効のリクエストを両方ともテストしたい場合、コントローラのメソッド実行の間で、レスポンスをリセットしてください。
controller.handleForm() // first execution … response.reset() … controller.handleForm() // second execution
Testing File Upload
ファイルアップロードのテスト
GrailsMockMultipartFile
class to test file uploads. For example consider the following controller action:
ファイルアップロードをテストするためには、GrailsMockMultipartFile
クラスを使います。例えば、次のコントローラアクションを見てください。
def uploadFile() { MultipartFile file = request.getFile("myFile") file.transferTo(new File("/local/disk/myFile")) }
GrailsMockMultipartFile
with the request:
このアクションをテストするには、リクエストにGrailsMockMultipartFile
を設定してください。
void testFileUpload() { final file = new GrailsMockMultipartFile("myFile", "foo".bytes) request.addFile(file) controller.uploadFile()assert file.targetFileLocation.path == "/local/disk/myFile" }
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.
GrailsMockMultipartFile
のコンストラクタの引数はファイル名とファイルの内容です。これはtransferTo
のモック実装をもっており、ファイルには書き込まずにtargetFileLocation
として値を保持します。
Testing Command Objects
Commnadオブジェクトのテスト
mockCommandObject
method. For example consider the following action:
mockCommandObject
メソッドにはCommandオブジェクトの処理をテストするための特別な機能があります。例えば、次のアクションを見てください。
def handleCommand(SimpleCommand simple) { if (simple.hasErrors()) { render "Bad" } else { render "Good" } }
これをテストするために、次のようにCommandオブジェクトをモック化した上で、データを投入して検証してください。
void testInvalidCommand() { def cmd = mockCommandObject(SimpleCommand) cmd.name = '' // doesn't allow blank namescmd.validate() controller.handleCommand(cmd)
assert response.text == 'Bad' }
Testing Calling Tag Libraries
タグライブラリの呼び出しテスト
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:
タグごとに必要となるテストの方法は異なりますが、タグライブラリの呼びしはControllerUnitTestMixin
を使用することでテストできます。例えば、message
タグを呼び出すテストでは、messageSource
にメッセージを加えます。次のアクションを見てください。
def showMessage() {
render g.message(code: "foo.bar")
}
これは次のようにテストをします。
void testRenderBasicTemplateWithTags() { messageSource.addMessage("foo.bar", request.locale, "Hello World")controller.showMessage()
assert response.text == "Hello World" }
12.1.2 タグライブラリのユニットテスト
The Basics
基本
grails.test.mixin.web.GroovyPageUnitTestMixin
mixin. To use the mixin declare which tag library is under test with the TestFor
annotation:
タグライブラリとGSPは、grails.test.mixin.web.GroovyPageUnitTestMixin
のMixinでテストできます。Mixinを使用するために、タグライブラリをTestFor
アノテーションの中に宣言します。
@TestFor(SimpleTagLib) class SimpleTagLibTests {}
ControllerUnitTestMixin
and the GroovyPageUnitTestMixin
using the Mock
annotation:
もし、コントローラからカスタムタグの呼び出しをテストする場合、Mock
アノテーションを使用してControllerUnitTestMixin
とGroovyPageUnitTestMixin
を組み合わせることができます。
@TestFor(SimpleController) @Mock(SimpleTagLib) class GroovyPageUnitTestMixinTests {}
Testing Custom Tags
カスタムタグのテスト
GroovyPageUnitTestMixin
class provides a mockTagLib()
method that you can use to mock a custom tag library. For example consider the following tag library:
Grailsコアのタグはテスト中に有効にする必要はありませんが、カスタムタグライブラリは有効にする必要があります。GroovyPageUnitTestMixin
クラスは、カスタムタグライブラリをモック化するために使えるmockTagLib()
メソッドを提供しています。
class SimpleTagLib {static namespace = 's'
def hello = { attrs, body -> out << "Hello ${attrs.name ?: 'World'}" }
def bye = { attrs, body -> out << "Bye ${attrs.author.name ?: 'World'}" } }
TestFor
and supplying the name of the tag library:
TestFor
を使用し、タグライブラリの名前を記載することによって、上記のタグライブラリをテストできます。
@TestFor(SimpleTagLib) class SimpleTagLibTests { void testHelloTag() { assert applyTemplate('<s:hello />') == 'Hello World' assert applyTemplate('<s:hello name="Fred" />') == 'Hello Fred' assert applyTemplate('<s:bye author="${author}" />', [author: new Author(name: 'Fred')]) == 'Bye Fred' } }
TestMixin
annotation and mock multiple tag libraries using the mockTagLib()
method:
代わりに、TestMixin
アノテーションとmockTagLib()
メソッドにより、複数のタグライブラリをモック化することができます。
@grails.test.mixin.TestMixin(GroovyPageUnitTestMixin) class MultipleTagLibraryTests {@Test void testMuliple() { mockTagLib(FirstTagLib) mockTagLib(SecondTagLib)
… } }
GroovyPageUnitTestMixin
provides convenience methods for asserting that the template output equals or matches an expected value.
GroovyPageUnitTestMixin
はテンプレート出力が期待した値と同じであることを検証するために便利なメソッドを提供しています。
@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
表示とテンプレートのレンダリングテスト
grails-app/views
via the render(Map)
method provided by GroovyPageUnitTestMixin
:
GroovyPageUnitTestMixin
で提供されているrender(Map)
メソッドを使ってgrails-app/views
内にあるビューとテンプレートのレンダリングをテストできます。
def result = render(template: "/simple/hello") assert result == "Hello World"
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.
これは、grails-app/views/simple/_hello.gsp
に配置されているテンプレートをレンダリングします。もしテンプレートがいくつかのカスタムタグライブラリに依存している場合、前の節で記載したmockTagLib
を呼び出す必要があります。
12.1.3 ドメインのユニットテスト
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.
モック機能のサポートは、非ドメインクラスがドメインクラスを使用している場合に、データベースを必要とせず、テストに集中できるという意味では最適な方法です。しかし、ドメインの永続化のテストを行いたい場合はHibernateとデータベースを使用するIntegrationテストが最適な方法です。
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:
ドメインクラスとしての振る舞いはDomainClassUnitTestMixin
を使用することで、データベースを必要とせずにテストを行うことができます。この実装はGORMの振る舞いを擬似した、メモリ上のConcurrentHashMap
です。この実装は実際のGORMの動作と比べて制限があることに注意してください。以下のHibernateに対するGORMの機能はIntegrationテストでのみテストできます。
- String-based HQL queries
- composite identifiers
- dirty checking methods
- any direct interaction with Hibernate
- 文字列ベースのHQLクエリ
- 複合主キー
- dirtyチェックメソッド
- Hibernateの直接操作
DomainClassUnitTestMixin
including:
上記以外の、よく使用されるGORM APIの大部分はDomainClassUnitTestMixin
を使用することでモック化することができます。
- Simple persistence methods like
save()
,delete()
etc. - Dynamic Finders
- Named Queries
- Query-by-example
- GORM Events
save()
やdelete()
といった永続化メソッド- ダイナミックファインダー
- 名前付きクエリ
- Exampleによるクエリ
- GORMのイベント
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.
もしサポートされてない部分があった場合でも、GrailsUnitTestMixin
のmockFor
メソッドを使用することで足りない部分をモック化することができます。または、テストの実行時間はかかりますが、完全にGrails環境が立ち上がるIntegrationテストを使用することもできます。
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:
DomainClassUnitTestMixin
は、通常コントローラやサービス、またはタグライブラリといった、Mock
アノテーションでドメインがモックとして協調動作する必要がある場所で使用されます。
import grails.test.mixin.*@TestFor(SimpleController) @Mock(Simple) class SimpleControllerTests {
}
SimpleController
class and mocks the behavior of the Simple
domain class as well. For example consider a typical scaffolded save
controller action:
上記の例ではSimpleController
のテストでSimple
ドメインクラスとして振る舞うモックを宣言しています。例えば典型的なスキャフォルドで生成されたコントローラのsave
アクションについて考えてみます。
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]) } } }
このアクションに対するテストは次のように書けます。
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:
複数のドメインのモックが必要な場合はMock
アノテーションにリストで指定することができます。
@TestFor(BookController) @Mock([Book, Author]) class BookControllerTests { … }
DomainClassUnitTestMixin
directly with the TestMixin
annotation:
別の方法としてTestMixin
アノテーションに直接DomainClassUnitTestMixin
を指定する方法もあります。
import grails.test.mixin.domain.DomainClassUnitTestMixin@TestFor(BookController) @TestMixin(DomainClassUnitTestMixin) class BookControllerTests { … }
mockDomain
method to mock domains during your test:
そしてテストの中でドメインをモック化するmockDomain
メソッドを呼び出します。
void testSave() { mockDomain(Author) mockDomain(Book) }
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:
mockDomain
メソッドは、ドメインを構築するマップの一覧を与えることで、データのフィクスチャを生成するような使い方もできます。
void testSave() { mockDomain(Book, [ [title: "The Stand", pages: 1000], [title: "The Shining", pages: 400], [title: "Along Came a Spider", pages: 300] ]) }
Testing Constraints
制約のテスト
There are 4 types of validateable classes:
- Domain Classes
- Classes Marked With The @Validateable Annotation
- Command Objects Which Have Been Made Valdiateable Automatically
- Classes Configured To Be Validateable via The
grails.validateable.classes
Config.groovy Property
The first 3 are easily testable in a unit test with no special configuration necessary as long as the test method is marked with @TestFor or explicitly applies the GrailsUnitTestMixin
using @TestMixin. See the examples below.
// src/groovy/com/demo/MyValidateable.groovy package com.demo@grails.validation.Validateable class MyValidateable { String name Integer age
static constraints = { name matches: /[A-Z].*/ age range: 1..99 } }
// grails-app/domain/com/demo/Person.groovy package com.democlass Person { String name
static constraints = { name matches: /[A-Z].*/ } }
// grails-app/controllers/com/demo/DemoController.groovy package com.democlass DemoController {
def addItems(MyCommandObject co) { if(co.hasErrors()) { render 'something went wrong' } else { render 'items have been added' } } }
class MyCommandObject { Integer numberOfItems
static constraints = { numberOfItems range: 1..10 } }
// test/unit/com/demo/PersonSpec.groovy package com.demoimport grails.test.mixin.TestFor import spock.lang.Specification
@TestFor(Person) class PersonSpec extends Specification {
void "Test that name must begin with an upper case letter"() { when: 'the name begins with a lower letter' def p = new Person(name: 'jeff')
then: 'validation should fail' !p.validate()
when: 'the name begins with an upper case letter' p = new Person(name: 'Jeff')
then: 'validation should pass' p.validate() } }
// test/unit/com/demo/DemoControllerSpec.groovy package com.demoimport grails.test.mixin.TestFor import spock.lang.Specification
@TestFor(DemoController) class DemoControllerSpec extends Specification {
void 'Test an invalid number of items'() { when: params.numberOfItems = 42 controller.addItems()
then: response.text == 'something went wrong' }
void 'Test a valid number of items'() { when: params.numberOfItems = 8 controller.addItems()
then: response.text == 'items have been added' } }
// test/unit/com/demo/MyValidateableSpec.groovy package com.demoimport grails.test.mixin.TestMixin import grails.test.mixin.support.GrailsUnitTestMixin import spock.lang.Specification
@TestMixin(GrailsUnitTestMixin) class MyValidateableSpec extends Specification {
void 'Test validate can be invoked in a unit test with no special configuration'() { when: 'an object is valid' def validateable = new MyValidateable(name: 'Kirk', age: 47)
then: 'validate() returns true and there are no errors' validateable.validate() !validateable.hasErrors() validateable.errors.errorCount == 0
when: 'an object is invalid' validateable.name = 'kirk'
then: 'validate() returns false and the appropriate error is created' !validateable.validate() validateable.hasErrors() validateable.errors.errorCount == 1 validateable.errors['name'].code == 'matches.invalid'
when: 'the clearErrors() is called' validateable.clearErrors()
then: 'the errors are gone' !validateable.hasErrors() validateable.errors.errorCount == 0
when: 'the object is put back in a valid state' validateable.name = 'Kirk'
then: 'validate() returns true and there are no errors' validateable.validate() !validateable.hasErrors() validateable.errors.errorCount == 0 } }
// test/unit/com/demo/MyCommandObjectSpec.groovy package com.demoimport grails.test.mixin.TestMixin import grails.test.mixin.support.GrailsUnitTestMixin import spock.lang.Specification
@TestMixin(GrailsUnitTestMixin) class MyCommandObjectSpec extends Specification {
void 'Test that numberOfItems must be between 1 and 10'() { when: 'numberOfItems is less than 1' def co = new MyCommandObject() co.numberOfItems = 0
then: 'validation fails' !co.validate() co.hasErrors() co.errors['numberOfItems'].code == 'range.toosmall'
when: 'numberOfItems is greater than 10' co.numberOfItems = 11
then: 'validation fails' !co.validate() co.hasErrors() co.errors['numberOfItems'].code == 'range.toobig'
when: 'numberOfItems is greater than 1' co.numberOfItems = 1
then: 'validation succeeds' co.validate() !co.hasErrors()
when: 'numberOfItems is greater than 10' co.numberOfItems = 10
then: 'validation succeeds' co.validate() !co.hasErrors() } }
For validateable classes which are not one of the first 3 types listed above but are configured with the grails.validateable.classes
property in Config.groovy
, one additional step is required to test validation. GrailsUnitTestMixin
provides a method named mockForConstraintsTests
that will mock validation support for these classes. See the example below.
// src/groovy/com/demo/Book.groovy package com.democlass Book { String title String author
static constraints = { author minSize: 5 } }
// grails-app/conf/Config.groovy grails.validateable.classes = [com.demo.Book]// ...
// test/unit/com/demo/BookSpec.groovy package com.demoimport grails.test.mixin.TestMixin import grails.test.mixin.support.GrailsUnitTestMixin import spock.lang.Specification
@TestMixin(GrailsUnitTestMixin) class BookSpec extends Specification {
void 'Test validation'() { given: mockForConstraintsTests Book
when: 'the author name has only 4 characters' def book = new Book() book.author = 'Jeff'
then: 'validation should fail' !book.validate() book.hasErrors() book.errors['author'] == 'minSize'
when: 'the author name has 5 characters' book.author = 'Jacob'
then: 'validation should pass' book.validate() !book.hasErrors() } }
Note that the mockForConstraintsTests
method changes the behavior of the errors object such that something like book.errors'author'
will evaluate to the name of the failed constraint, not a org.springframework.validation.FieldError
object. This is convenient for unit tests. If your unit test really does want a reference to the org.springframework.validation.FieldError
object use something like book.errors.getFieldError('author')
.
That's it for testing constraints. One final thing we would like to say is that testing the constraints in this way catches a common error: typos in the "constraints" property name which is a mistake that is easy to make and equally easy to overlook. A unit test for your constraints will highlight the problem straight away.
12.1.4 フィルタのユニットテスト
class CancellingFilters { def filters = { all(controller:"simple", action:"list") { before = { redirect(controller:"book") return false } } } }
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:simple
コントローラのlist
アクションをインターセプトし、book
コントローラにリダイレクトします。このフィルタをテストするには、まずSimpleController
クラスを対象としたテストを作り、CancellingFilters
をモックコラボレータとして設定します:@TestFor(SimpleController) @Mock(CancellingFilters) class SimpleControllerTests {}
withFilters
method to wrap the call to an action in filter execution:withFilters
メソッドで囲むようにテストを実装します:void testInvocationOfListActionIsFiltered() {
withFilters(action:"list") {
controller.list()
}
assert response.redirectedUrl == '/book'
}
action
parameter is required because it is unknown what the action to invoke is until the action is actually called. The controller
parameter is optional and taken from the controller under test. If it is another controller you are testing then you can specify it:action
パラメータが必須であることに注意してください。
controller
パラメータは省略可能です。省略時はテスト対象のコントローラが適用されます。
もしテストしているのが別のコントローラである場合はそれを指定します。withFilters(controller:"book",action:"list") { controller.list() }
12.1.5 URLマッピングのユニットテスト
The Basics
基本
TestFor
annotation testing a particular URL mappings class. For example to test the default URL mappings you can do the following:
URLマッピングをテストするには、TestFor
アノテーションを使用します。例えば、デフォルトのURLマッピングをテストするためには次のように使用します。
import org.example.AuthorController import org.example.SimpleController@TestFor(UrlMappings) @Mock([AuthorController, SimpleController]) class UrlMappingsTests { … }
@Mock
annotation.
上記のように、テストを実施するURLマッピングの対象となるコントローラにはMock
アノテーションを追加しなければなりません。
Note that since the default UrlMappings
class is in the default package your test must also be in the default package
UrlMappings
クラスがデフォルトパッケージの中にあるので、あなたが実行するテストもデフォルトパッケージの中になければいけません。
grails.test.mixin.web.UrlMappingsUnitTestMixin
for testing URL mappings. These include:
URLマッピングをテストするためにgrails.test.mixin.web.UrlMappingsUnitTestMixin
には以下のような便利なメソッドが定義されています。
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)
assertForwardUrlMapping
- 指定したコントローラクラスにフォワードするURLマッピングを検証する(コントローラはモックコラボレータとして定義される必要があることに注意してください)
assertReverseUrlMapping
- Asserts that the given URL is produced when reverse mapping a link to a given controller and action
assertReverseUrlMapping
- 指定したURLが、与えられたコントローラとアクションからリバースマッピングしたリンクとして生成されるか検証する
assertUrlMapping
- Asserts a URL mapping is valid for the given URL. This combines theassertForwardUrlMapping
andassertReverseUrlMapping
assertions
assertUrlMapping
- 指定したURLに対応するURLマッピングの妥当性を検証する。これは、assertForwardUrlMapping
とassertReverseUrlMapping
を組み合わせたものです。
Asserting Forward URL Mappings
URLマッピングの転送の検証
assertForwardUrlMapping
to assert that a given URL maps to a given controller. For example, consider the following URL mappings:
指定したURLが、期待したコントローラにマップされるか検証するためにassertForwardUrlMapping
が使用できます。例えば、以下のようなURLマッピングについて考えてみます。
static mappings = { "/action1"(controller: "simple", action: "action1") "/action2"(controller: "simple", action: "action2") }
このようなURLマッピングのテストは次のように書けます。
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
URLマッピングのリバース検証
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
.
GSPのビューでlink
タグを使用した場合に、URLマッピングから生成されるリンクを確認(チェック)するためにassertReverseUrlMapping
を使用できます。
assertReverseUrlMapping
の代わりにassertForwardUrlMapping
を使用する以外は上記の例とほとんど同じです。
assertUrlMapping
はこれら2つのアサーションを組み合わせた検証が出来ます。
Simulating Controller Mapping
コントローラのマッピングをシミュレート
UrlMappings
as a mock collaborator and the mapURI
method. For example:
URLマッピングの妥当性をチェックするためのアサーションに加えて、UrlMappings
をモックとして指定しmapURI
メソッドを使用することで、コントローラへのマッピングをシミュレートすることができます。
@TestFor(SimpleController) @Mock(UrlMappings) class SimpleControllerTests {void testControllerMapping() {
SimpleController controller = mapURI('/simple/list') assert controller != null
def model = controller.list() assert model != null } }
12.1.6 コラボレータのモック化
mockFor()
method that is available when using the TestFor
annotation. The signature of mockFor
is:
mockFor()
メソッドがあります。これはTestFor
アノテーションを使用している場合にも使用できます。mockFor
は以下のように使用します。mockFor(class, loose = false)
mockFor
を使用したモックでは、demand(要求)がstrict(厳密)、またはloose(緩やか)のどちらであるかを指定する必要があります。def strictControl = mockFor(MyService) strictControl.demand.someMethod(0..2) { String arg1, int arg2 -> … } strictControl.demand.static.aStaticMethod {-> … }
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.
mockControl.createMock()
メソッドを呼ぶことで、モックインスタンスを作成します。複数のモックインスタンスが必要な場合は、必要なだけこのメソッドを呼び出すことで、複数のモックインスタンスが作成できます。また期待するメソッドの呼び出しを検証するにはmockControl.verify()
を呼び出してください。demandExplicit
method that can be used in place of demand. This will check the mocked class's metaClass and throw an ExplicitDemandException if a method with that name and signature doesn't exist. For example, given the service:
demandExplicit
メソッドも提供しています。これはモックするクラスのmetaClassをチェックし、もしそのメソッドの名前、シグネチャが存在しない場合に、ExplicitDemandExceptionをスローします。例えば次のようなサービスがあるとします。class MyService {
def someMethod(String s) { … }
}
def strictControl = mockFor(MyService)
//Works just like the demand method since method signature exists on the class
strictControl.demandExplicit.someMethod(1) { String arg1 }
def strictControl = mockFor(MyService) //Throws ExplicitDemandException because method signature doesn't exist strictControl.demandExplicit.someMethod(1) { String arg1, String arg2 }
def looseControl = mockFor(MyService, true)
12.1.7 コーデックのモック化
GrailsUnitTestMixin
provides a mockCodec
method for mocking custom codecs which may be invoked while a unit test is running.
GrailsUnitTestMixin
はカスタムコーデックのモックを生成するためのmockCodec
メソッドを提供しています。このメソッドはユニットテストの実施中に呼び出される可能性があります。
mockCodec(MyCustomCodec)
コーデックのモック生成に失敗している場合は、ユニットテストがMissingMethodException
で終了する可能性があります。
12.2 インテグレーションテスト
class MyServiceTests extends GroovyTestCase { void testSomething() { log.info "Starting tests" … } }
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()
.log
のプロパティにはjava.util.logging.Logger
のインスタンスが設定され(Grailsによって注入されるのではなく、ベースクラスから継承される)、アプリケーション側で注入されるlog
プロパティと同じメソッドは持っていません。例えば、debug()
やtrace()
メソッドがなく、warn()
(と同じ意味を持つもの)がwarning()
になります。トランザクション
transactional
property to your test class to check transactional behaviour:transactional
プロパティをテストクラスに追加することで、トランザクション制御を抑止することができます:class MyServiceTests extends GroovyTestCase { static transactional = falsevoid testMyTransactionalServiceMethod() { … } }
tearDown
method, so these tests don't interfere with standard transactional tests that expect a clean database.tearDown
メソッドなどで、永続化されたすべてのデータを削除するようにしてください。コントローラのテスト
class FooController {def text() { render "bar" }
def someRedirect() { redirect(action:"bar") } }
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 } }
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.response
はMockHttpServletResponse
のインスタンスです。このインスタンスでは、レスポンスを書き出すときであればcontentAsString
により生成されたコンテンツが取得でき、リダイレクトの場合はリダイレクトされたURLが取得できます。これらのServlet APIのモックバージョンは(実際のAPIとは異なり)何でも変更可能で、contextPath
などリクエストのプロパティについても設定可能です。サービスを伴うコントローラのテスト
class FilmStarsController { def popularityServicedef update() { // do something with popularityService } }
class FilmStarsTests extends GroovyTestCase { def popularityServicevoid testInjectedServiceInController () { def fsc = new FilmStarsController() fsc.popularityService = popularityService fsc.update() } }
コマンドオブジェクトを使っているコントローラのテスト
class AuthenticationController { def signup(SignupForm form) { … } }
def controller = new AuthenticationController() controller.params.login = "marcpalmer" controller.params.password = "secret" controller.params.passwordConfirm = "secret" controller.signup()
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.signup()
を呼び出しているのを見て、モック化されたリクエストパラメータからなんと自動的にコマンドオブジェクトに値を渡してくれます。ちなみに、コントローラのテストにおいて、Grailsが用意したモック化されたリクエストのparams
は変更可能です。コントローラとレンダーメソッドのテスト
def save() { def book = Book(params) if (book.save()) { // handle } else { render(view:"create", model:[book:book]) } }
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:modelAndView
プロパティの中には保存されています。modelAndView
プロパティはSpring MVCのModelAndViewクラスで、アクションの結果をテストするのに利用できます:def bookController = new BookController()
bookController.save()
def model = bookController.modelAndView.model.book
リクエストデータをシミュレートする
def create() {
[book: new Book(params.book)]
}
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 }
void testCreateWithJSON() {def controller = new BookController()
controller.request.contentType = "application/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 theclass
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.
JSONを用いる場合、class
プロパティにて結びつけるターゲットの型の名前を指定することを忘れてはいけません。XMLでは暗黙的に<book>
ノードの名前が使われますが、JSONではこのプロパティがJSONパケットの一部として必要になります。
Webフローのテスト
grails.test.WebFlowTestCase
which subclasses Spring Web Flow's AbstractFlowExecutionTests class.grails.test.WebFlowTestCase
と呼ばれる特別なテストハーネスが必要になります。
Subclasses of WebFlowTestCase
must be integration tests
WebFlowTestCase
のサブクラスは、必ずインテグレーションテストでなければなりません。
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") } … } }
getFlow
method:getFlow
をオーバライドすることにより、"フロー定義"を使っていることをテストハーネスに知らせる必要があります:import grails.test.WebFlowTestCaseclass ExampleFlowTests extends WebFlowTestCase { def getFlow() { new ExampleController().exampleFlow } … }
getFlowId
method, otherwise the default is test
:getFlowId
メソッドをオーバライドして、フローIDを指定することができます。指定しないならデフォルトとしてtest
が使われます:
import grails.test.WebFlowTestCaseclass ExampleFlowTests extends WebFlowTestCase { String getFlowId() { "example" } … }
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) }
startFlow
method:startFlow
メソッドによりフローを開始します:void testExampleFlow() { def viewSelection = startFlow() … }
signalEvent
method to trigger an event:signalEvent
を使います:void testExampleFlow() { … signalEvent("go") assert "next" == flowExecution.activeSession.state.id assert "world" == flowScope.hello }
hello
variable into the flow scope.hello
変数をフローのスコープに設定します。タグライブラリのテスト
StreamCharBuffer
but this class implements all of the methods of String
). So for example if you have a tag library like this:String
の全てのメソッドを実装したStreamCharBuffer
となります)が返されるので単純です。例えば、タグライブラリのテストはこのようになります:class FooTagLib {def bar = { attrs, body -> out << "<p>Hello World!</p>" }
def bodyTag = { attrs, body -> out << "<${attrs.name}>" out << body() out << "</${attrs.name}>" } }
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() } }
testBodyTag
, we pass a block that returns the body of the tag. This is convenient to representing the body as a String.testBodyTag
の例を見てください。この例ではタグのボディを返すブロックを渡しています。これはタグ本体を文字列として表現するのに便利です。GroovyPagesTestCaseを用いたタグライブラリのテスト
grails.test.GroovyPagesTestCase
class to test tag libraries with integration tests.grails.test.GroovyPagesTestCase
クラスを用いたタグライブラリのテストが可能です。GroovyPagesTestCase
class is a subclass of the standard GroovyTestCase
class and adds utility methods for testing the output of GSP rendering.GroovyPagesTestCase
クラスは標準的なGroovyTestCase
クラスのサブクラスであり、GSPレンダリングの出力をテストするためのユーティリティメソッドが追加されています。
GroovyPagesTestCase
can only be used in an integration test.
GroovyPagesTestCase
はインテグレーションテストの中だけで使えます。
import java.text.SimpleDateFormatclass FormatTagLib { def dateFormat = { attrs, body -> out << new SimpleDateFormat(attrs.format) << attrs.date } }
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]) } }
applyTemplate
method of the GroovyPagesTestCase
class:GroovyPagesTestCase
クラスのapplyTemplate
メソッドを使ったGSPの結果も得ることもできます: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 } }
ドメインクラスのテスト
void testQuery() { def books = [ new Book(title: "The Stand"), new Book(title: "The Shining")] books*.save()assertEquals 2, Book.list().size() }
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:Book
インスタンスが呼び出されたときには実際には保存されていないので失敗するでしょう。save
を呼び出しても単にHibernateに未来のある時点でインスタンスが保存されるべきであることを示唆するだけです。ただちに変更をコミットするためには"フラッシュ"をします:void testQuery() { def books = [ new Book(title: "The Stand"), new Book(title: "The Shining")] books*.save(flush: true)assertEquals 2, Book.list().size() }
flush
with a value of true
the updates will be persisted immediately and hence will be available to the query later on.flush
にtrue
を渡しているので、データの更新はただちに保存され、後のクエリでも利用可能になります。
12.3 ファンクショナルテスト
Functional tests involve making HTTP requests against the running application and verifying the resultant behaviour. The functional testing phase differs from the integration phase in that the Grails application is now listening and responding to actual HTTP requests. This is useful for end-to-end testing scenarios, such as making REST calls against a JSON API.Canoo Webtest
- http://grails.org/plugin/webtestG-Func
- http://grails.org/plugin/functional-testGeb
- http://grails.org/plugin/gebSelenium-RC
- http://grails.org/plugin/selenium-rcWebDriver
- http://grails.org/plugin/webdriver
共通オプション
inlineオプション
-inline
option specifies that the grails application should be started inline (i.e. like run-app
).-inline
オプションが指定されるとGrailsアプリケーションがインライン起動されます(例えばrun-app
のような)。baseUrl
or war
options are set-inline
オプションは-baseUrl
か-war
のどちらかが設定されていないと、暗黙的に有効になりますwarオプション
-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.-war
オプションを指定すると、Grailsアプリケーションはwarファイルにパッケージ化した後に起動されます。このオプションはアプリケーションを実際にプロダクトと同じ状態でテストすることができるので便利です。しかし、-inline
オプションよりも起動に時間がかかります。また、warで起動するとフォークされたJVM上で実行されます。これは内部のオブジェクトにアクセスできないことを意味しています。grails test-app functional: -war
httpsオプション
-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.-https
オプションはhttpリクエストと同様にアプリケーションがhttpsリクエストを受け付けられるようにします。このオプションは-inline
と-war
の両方のオプションと一緒に使うことができます。grails test-app functional: -https
-httpsBaseUrl
option is also given.-https
オプションを使っていてもテストで使われる base url はhttpsにはなりません。-httpsBaseUrl
をあわせて指定しない限り、httpのままとなります。httpsBaseUrlオプション
-httpsBaseUrl
causes the implicit base url to be used for tests to be a https url.-httpsBaseUrl
オプションを使えば暗黙的に設定されている base url がテスト時にhttpsのURLとして使われます。grails test-app functional: -httpsBaseUrl
-baseUrl
option is specified.-httpsBaseUrl
オプションは-baseUrl
オプションが指定されると無視されます。baseUrlオプション
baseUrl
option allows the base url for tests to be specified.-baseUrl
オプションによりテスト実施時の base url を指定することができます。grails test-app functional: -baseUrl=http://mycompany.com/grailsapp
-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.-baseUrl
オプションが指定されていると、ローカル環境でのGrailsアプリケーションを-inline
や-war
を指定しないで起動することができなくなります。ローカル環境でのGrailsアプリケーションで base url を指定するには、必ず-inline
か-war
のどちらかのオプションを指定する必要があります。