2009年12月23日水曜日

Grailsのtest-appで、コンソールにstacktraceを出す

Grailsのバージョンは1.2.0.RC2

Grailsには、組み込みのテスト実行環境がついています。
Grailsをインストールして、Grailsのプロジェクトを作成して、
コンソールで"test-app"と打ち込むだけでテストが実行されます。

んですが、このテスト、実行してエラーがでても、コンソールにスタックトレースがでないんですよね。

testSample...FAILED

つー、そっけない一行がでるだけ。なんでやねん。

というわけで、Grailsのテストイベントを取ってスタックトレースを出してみます。

1.スタックトレースを出力するためのイベントリスナを作成する。
以下のようなスタックトレース出力クラスを作ります
package testsupport
import grails.build.GrailsBuildListener;
public class StackTraceDumper implements GrailsBuildListener {
void receiveGrailsBuildEvent(String name, Object[] args) {
if (name == 'TestFailure') {
this.doTestFailure(*args)
}
}
protected doTestFailure(String name, failure, boolean isError) {
failure?.printStackTrace()
}
}

2.上記イベントリスナをコンパイル
なんか、イベントリスナだけは前もってコンパイルしとかないと失敗しました。
[grails-project]/src/groovyの下に入れて"grails compile"を実行してもいいし、
jarにして[grails-project]/libにいれてもいいと思います。

3._Events.groovyスクリプトの記述
上記イベントリスナを設定します。
[grails-project]/scripts/_Events.groovyに以下を記述
import testsupport.StackTraceDumper
eventListener.addGrailsBuildListener(new StackTraceDumper())

4.test-appコマンドを、-echoErrオプション付きで実行する
Grails1.2.0から、echoErrオプションで標準エラーを出力できるようになりました。
"test-app -echoErr"と激しく打ち込み。
テストクラスを限定したり、ユニットテストだけ実行したりできます
"test-app [TEST CLASS] -echoErr"
"test-app unit: -echoErr"

で、スタックトレースがでるはず…


ちなみに、spockプラグインを使うと、powerassertsっぽいかっこいいスタックトレースになります。
こんな↓


あ、[grails-project]/scripts/_Events.groovyの仕様とかよく分かってないんで、突っ込みどころあったら突っ込んでくらはい

2009年12月8日火曜日

[Grails] GSP部分テンプレート内のitがときどき消滅する件

grailsのバージョンは1.2-M4

grailsで部分テンプレート「/share/_book.gsp」を表示するとき、



とかやると思います。
このとき、部分テンプレート「/share/_book.gsp」内の変数「it」にbookが自動で設定されるわけですが、
この「it」にアクセスできないことがあるようです。

具体的には、部分テンプレート「/share/_book.gsp」内で

${it.title.encodeAsHTML()}

というふうに、linkタグを使っているときにlinkタグテキスト部内のit(it.title.encodeAsHTML())にアクセスできないみたいです。この場合、次のようにdelegateからアクセスする必要があります。

${delegate.it.title.encodeAsHTML()}


テキスト部は、クロージャになってタグライブラリの引数に渡されるので、delegateの差し替えなどしていない場合上のようになる…のかな?

2009年11月6日金曜日

Grails + easyb でイミフなエラー(MultipleCompilationErrorsException)が出る件

Grailsのバージョンは1.2-M3
EasyB + Grails Testingプラグイン(grails-easyb)のバージョンは1.1

ちょっと前、GrailsでRSpec的なBDDができたらいいなぁ、的なこと言ってたんですが、どうやら"easyb" というBDDフレームワークがあって、Grailsでも使用できる模様。([Java][Groovy][BDD] Grails におけるドメインクラスのユニットテスト - Grails標準テストと Easyb プラグイン使用)

人によるかもですが、私の場合xUnit系のテストケースよりはBDDのスペックの方が断然書きやすいので、早速飛びついてみました。
easyb公式ページなどを参考に、簡単なシナリオを書いてみる…

で、タイトル通り怒られました。このメッセージがよく分からない…
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed, Script1.groovy: 7: unexpected token: } @ line 7, column 5.
グーグル先生とかに相談してみたんですがさっぱり。
適当に試行錯誤してみました。まずコレ。
scenario "TestScenario", {
given "TestGiven", {
}
when "TestWhen", {
}
then "TestThen", {
"test".shouldBe("test")
}
}
このコードなら通してくれました。
だから何?って感じの超テストコードなんですが、ここから段々デコレーションしてみます。

といいますか、どうやら日本語が含まれてるとだめっぽい。コメントすら無理
でもシナリオ名に日本語使ったとき、たまに通ることもある…

あーもうだめだ、再現性ないとかお試しで頑張るには手強すぎる。
シナリオの書き方が違ってるのか?コメントはどこどこに入れてはいけないとか…そんなバ
サービスのインジェクションとかどうするのかなー、とか他にもいろいろ調べたかったんですけど、とりあえず様子見ということで。

しばらく標準のテストで進めるか…

2009年11月3日火曜日

[Grails] Integration Test のテストケースは、GrailsUnitTestCaseでなくGroovyTestCase を継承するらしい

Grailsのバージョンは1.2-M3

情報元はここ
http://agileice.blogspot.com/2009/09/grails-integration-testing.html

なんか、GrailsのIntegrationTestで、Config.groovyに設定した定義情報が使えないなあ、って思ってネットサーフィンしてたら、上記のページを発見。

テストケースの継承元を間違うと、テストケースのtearDownでConfig.groovyの設定情報がnullになってしまうらしいです。
  • GrailsUnitTestCaseとか、それ継承してるControllerUnitTestCaseとか
    Unitテストのみに使用
  • GroovyTestCase
    Integration Test で使用
ポイントは、Integration Testのテストケース全部にGroovyTestCase を使うことですかね。
一個でもGrailsUnitTestCaseのが混じってると、その後のテストケースで設定情報が消えます。

最近のGrailsだと、コントローラーやサービス作成時にUnit Testを生成するから、そのファイルをIntegration Testの方に移動したりしているうちに段々ごっちゃになってそう。

2009年10月29日木曜日

Grails+Acegi プログラム上でログイン・ログアウトさせる

Grailsのバージョンは1.2-M3
Acegi Security は 0.5.2

忘れないうちにメモ

import org.springframework.security.providers.UsernamePasswordAuthenticationToken as AuthToken
import org.springframework.security.context.SecurityContextHolder as SCH

class NantokaService {
def daoAuthenticationProvider

// ログイン処理
void systemLogin(String username, String password) {
def auth = new AuthToken(username, password)
def authtoken = daoAuthenticationProvider.authenticate(auth)
SCH.context.authentication = authtoken
}
// ログアウト処理
void systemLogout() {
SCH.context.authentication = null
}
}

こんな感じ

2009年10月28日水曜日

7 〜モールモースの騎兵隊〜

をいまさらプレイしてます。

いまさらすぎるタイトルですが、ヴィーナス&ブレイブス買いに行ったら500円で売っていたので。
事前にプレイしとくとちょっぴり嬉しい、とどこかで聞いたような気が…

戦闘システムにランダム要素ほとんどないんで、考えがいあって楽しいです。
一問一問出題されるクイズみたいな戦闘だし、飽きないで遊べてます。
これでランダムエンカウント、とかだったら途中で投げそう。

しかし、この騎士団の連中、プライドだけ高い無能集団みたいな描かれ方してますけど、
構成メンバー全員、職種「騎士」なんだよなぁ。
それ最強集団じゃないスか?
適当にローテーションしてるだけで大抵殲滅できそう。

2009年10月27日火曜日

GrailsのUrlMappingsから直接viewに飛ばすと、localeの読み込みがおかしくなる

Grailsのバージョンは1.2-M3

ここで四時間くらい詰まった…

開発中アプリのトップページ「localhost/sampleapp/」に、"messages_ja.properties"から読み込んだメッセージを表示させていたんです。
で、URLの末尾に「?lang=de」とかつけるとそのメッセージがドイツ語(messages_de.properties)に変わるはずじゃないですか。

でも、変わらなかったんですよね。ブラウザの設定から日本語を消しても、クッキーを消しても、ずっと日本語表示のまま。

こういうときはGoogleいったあと、JIRAでしょうか。
http://jira.codehaus.org/browse/GRAILS-3871
に原因が書いてました。

Grailsのデフォルトだと、UrlMappingsに
"/"(view:"/index")

って書いてて、トップーページをviewに直接遷移させてるんですけど、
この場合、そのviewでlocaleの変更ができなくなるみたい。

いちど
"/"(controller:"foo")
でコントローラーを呼び出して、その中でindex.gspをrenderするように修正することで直りました。


だいぶ有名な事象みたいだけど、これ、直るのかな…
JIRAみると、必殺技「仕様です」みたいにも読めるけど。

GrailsのTagLibテストケースの記述

Grailsのバージョンは1.2-M3

最近のフレームワークは、開発者がテストにハマるようにいろいろ趣向こらしていて助かりますよね。
RSpec on RailsとZenTestはもうすごいよかったなぁ…
Groovyでも似たようなのないですかね、RSpec on Grails みたいなヤツ。あとZenTestみたいなヤツ。

さて、Grailsも最近のフレームワークなので、テストは結構楽しくできます。
ですが、Grailsの1.2(1.1も?)だと、ドメインモデルだのコントローラーだのは、テストケースの中でnewしてもうまくいかないらしいです。(ダイナミックメソッドが注入されずエラーになるみたい 参考:Grails1.1.xのUnitテスト)

TagLibのテストでも、同じようにnewして実行すると、メソッドやプロパティが見つからないと言われて落ちることがあります。
TagLibのユニットテストを記述するTagLibUnitTestCaseには、tagLibプロパティがあって、対象となるTagLibがセット済みなのでそれを使えばいいみたいです。

Grails をマスターするのカスタムタグを例にすると、

×
class DateTagLibTests extends GroovyTestCase {
def dateTagLib

void setUp(){
dateTagLib = new DateTagLib()
}

void testThisYear() {
String expected = Calendar.getInstance().get(Calendar.YEAR)
assertEquals("the years don't match", expected, dateTagLib.thisYear())
}
}

class DateTagLibTests extends GroovyTestCase {
void testThisYear() {
String expected = Calendar.getInstance().get(Calendar.YEAR)
assertEquals("the years don't match",expected, tagLib.thisYear())
}
}

上だと、
groovy.lang.MissingPropertyException:No such property: out for class
とか言われて落ちました。

あ、でも各テストケースの中(setUpじゃなくて)でTagLibクラスをnewすると普通にテスト通りましたね。
よくわからんです。

WebFlowってRESTfulじゃないよね…

すくなくとも、Grails(てかSpring?)の実装だと。
まったく同じ画面なのに、リロードするとどんどんURLが変化していく…
あれ、複数のURLが同じリソースを指すのはいいんだっけ。でもWebFlowのURLって特に記述的な意味とかないし。
POSTのリクエストボディに制御パラメータ入れられないかな。

Grailsが最近取り入れてるRESTfulなURLマッピングと組み合わせようとすると、訳分からんことに。
なにかうまい方法ないでしょうか。よくわからない…
とりあえず、WebFlowについてよく理解できるまでは使わないようにします。

Grailsでのメール認証つきユーザー登録

Grails バージョンは1.2-M3

ユーザー登録画面

「仮登録メールを送信しました、本登録にはこのリンクにアクセスして云々」

本登録が完了しました

という、よくあるやつです。


次のふたつのプラグインで実現
  • 定番らしいユーザー認証プラグイン「Spring(Acegi) Security 0.5.2」(公式)
  • メールアドレス確認用プラグイン「Email Confirmation 1.0.3」(公式)
まあ、「Email Confirmation」のチュートリアルをやってみたら、なんとなく感じがつかめそうですが、実装はこんな感じで。

1.「Email Confirmation」にメールアドレス、入力されたユーザー情報(ログインIDとか)を渡す
[PersonController#save]
emailConfirmationService.sendConfirmation("sendto@sample.com", "メール本文", [from:"from@sample.com", view:"/path/to/mail-body-gsp"], "${loginid}\t${encordedPassword}")
emailConfirmationServiceはプラグインが提供しているServiceです。
こうすると "sendto@sample.com"にメールアドレス確認用のリンクがかかれたメールが送られます。最後に"${loginid}\t${encordedPassword}"でユーザー情報を登録してます。

2.「Email Confirmation」のアドレス確認完了イベントでユーザー登録
さて、メールに送信されたリンクを押すと、emailConfirmationServiceのonConfirmationイベントが発生します。
このイベントにはリスナとしてクロージャを登録するのですが、このクロージャの中でさっき登録した"${loginid}\t${encordedPassword}"がとれるので、分解してユーザーを構築してDBに格納して終了です。

Grailsのユーザー認証、認可用プラグインには今回使用したSpringSecurityの他にも、JSecurity(もう非推奨、今後はApache Shiro(何?)プラグインを使えといわれた)、Authentication Pluginなどいろいろあるんですが、どれ使っても同じ用に実装すればできるのかな? (ほとんど触ってないので分からないですが。)
というか、この辺のプラグインだとEmail Confirmation使わずにできたりしないかな…