2012年1月7日土曜日

Grails2.0のユニットテスト用アノテーションを整理してみる

Grails Version
2.0.0

Grails 2.0から、アノテーションを使ってユニットテストを書くことになってるのですが、
そのアノテーションの使い方が少しややこしかったので整理してみました。あんまり自信ないです。

Grailsのテストで使うアノテーションは以下のとおり。

  • grails.test.mixin.TestMixin
  • grails.test.mixin.Mock
  • grails.test.mixin.TestFor

@TestMixin

使用例
@TestMixin(GrailsUnitTestMixin)

テストクラスに以下のようなミックスインを適用して、テスト用、モック作成用のメソッドやプロパティを追加、設定するのに使用する。

リストを渡せば複数のミックスインを適用できる。

  • 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

以下にいくつかの使用例を挙げる。

GrailsUnitTestsMixin

以下のような機能をテストクラスに追加する、全てのミックスインの基盤となるミックスイン

  • grailsApplicationやmessageSourceなどのGrailsのコンテキストプロパティを追加
  • mockForConstraintsTests、mockForなどの基本的なモック作成メソッドを追加
import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import org.junit.Test

@TestMixin(GrailsUnitTestMixin)
class GenericTests {
    
    @Test
    void genericTest() {
        mockForConstraintsTests(Book)
        def book = new Book(title: "")
        assert !book.validate()
        assert book.errors["title"] == "blank"
    }
}

DomainClassUnitTestMixin

さっきの「GrailsUnitTestsMixin」を適用し、さらにドメインクラスのテスト用にmockDomainメソッドを追加する。

import grails.test.mixin.TestMixin
import grails.test.mixin.domain.DomainClassUnitTestMixin
import org.junit.Test

@TestMixin(DomainClassUnitTestMixin)
class GenericTests {
    
    @Test
    void genericTest() {
        mockDomain(Book)
        def book = new Book(title: "")
        assert !book.save()
        assert book.errors.getFieldError("title").code == "blank"
    }
}

ServiceUnitTestMixin

さっきの「GrailsUnitTestsMixin」を適用し、さらにサービスのテスト用にmockServiceメソッドを追加する。

import grails.test.mixin.TestMixin
import grails.test.mixin.services.ServiceUnitTestMixin
import org.junit.Test

@TestMixin(ServiceUnitTestMixin)
class GenericTests {
    @Test
    void genericTest() {
        mockService(HelloService)
        assert applicationContext.helloService.hello() == "Hello, World"
    }
}

ControllerUnitTestMixin

「GrailsUnitTestsMixin」を適用し、さらにコントローラーのテスト用にmockControllerメソッドやサーブレットコンテキストのモックなどを追加する。

import grails.test.mixin.TestMixin
import grails.test.mixin.web.ControllerUnitTestMixin
import grails.test.mixin.services.ServiceUnitTestMixin
import org.junit.Test

@TestMixin([ControllerUnitTestMixin, ServiceUnitTestMixin])
class GenericTests {
    @Test
    void genericTest() {
        mockService(HelloService)
        
        def controller = mockController(BookController)
        controller.show()

        assert response.text == "Hello, World"
    }
}

などなど。

@Mock

使用例
@Mock(BookController)
  • 指定クラスに応じて、適当にミックスインを適用する
  • 更に指定クラスのモックを作成してコンテキストに登録する

例えば……

  • ドメインクラスの場合: DomainClassUnitTestMixinを適用し、更に指定クラスに対してmockDomainを適用する
  • サービスクラスの場合: ServiceUnitTestMixinを適用し、更に指定クラスに対してmockServiceを適用する
  • コントローラークラスの場合: ControllerUnitTestMixinを適用し、更に指定クラスに対してmockControllerを適用する
  • などなど
import org.junit.Test
import grails.test.mixin.Mock
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes

@Mock([BookController, HelloService])
class GenericTests {
    
    @Test
    void genericTest() {
        def controller = webRequest.currentRequest.getAttribute(GrailsApplicationAttributes.CONTROLLER)
        controller.show()
        assert response.text == "Hello, World"
    }
    
}

@TestFor

使用例
@TestFor(BookController)
  • 指定クラスに対して@Mock相当の機能を適用してモックを作成
  • 指定クラスのモックに、アーティファクト名のプロパティでアクセスできるようにする
  • testで始まるメソッドに、@Testアノテーションを付加

このアノテーションには、複数のクラスを引数に渡すことができない。

import grails.test.mixin.Mock
import grails.test.mixin.TestFor

@TestFor(BookController)
@Mock(HelloService)
class GenericTests {

    void testGenericTest() {
        controller.show()
        assert response.text == "Hello, World"
    }
    
}

ちなみに

Grailsのコマンド、test-appを使ってテストを実行すると、いくつかの処理を自動で行ってくれるようです。

例えば、ドメインクラスがBookで、テストクラスがBookTestsという名前だった場合、BookTestsに@TestFor(Book)が何も書かなくても適用されるように見えます。

この辺の動作はまだよく分かりません。

おわりに

もっぱら使用するのは@TestForと@Mockであって、@TestMixinで直接テストメソッドをミックスインすることはあまりないと思いますが、大体こんな役割になってそう、ということです。

動作確認はしましたが、ソースはあまり追っていないので間違ってるところもあるかもしれません。

気がついたことなどありましたら、教えてもらえると幸いです。

0 件のコメント:

コメントを投稿