ラベル テスト の投稿を表示しています。 すべての投稿を表示
ラベル テスト の投稿を表示しています。 すべての投稿を表示

2012年12月28日金曜日

Spockで例外のテスト

年末ということで広島に来ているみけです。

Spockとは


まあ、ググってください。


例外のテストがうまくいかなんだ…


したがってSpockはJUnitで@RunWith (Theories.class)でやるようなテストを


非常に見やすい形で、かつ型安全に実行できるわけです。


そこで、例外のテストを今度は書いてみることにしました。



テストを実行すると…




落ちたよ(´・ω・`)

Spockは例外を扱うようなテスト書けないのか…残念無念orz


しかたがないので@RunWith (Theories.class)使ったよ…


というわけで、Spockを諦めてJUnitへ…

なんか、@RunWith (Theories.class)使うと、

@DataPointsでデータを指定できるのですが、

なんか配列を強制されてるっぽいので、

個人的にはイケてない気がする…
(まあ、便利ですけど、それとも僕の書き方が悪いのか…)
(求むツッコミ!)




で、テスト実行、テストクリア!




ということで結論…




と、そこでいろふさん登場。



ふむ、thrown!そんなシンタックスがあるのか!

というわけで、Spockのページを読むと、確かにあった。





Spockで例外のテスト


Spockのページを参考にテストを書きなおしてみると、

こんなんなった。





そこでテスト実行。




通った!通ったよー!


結論


出来上がったソースを見てみると、

境界値テストとかのデータパターンを書いてテストするのに

条件と期待値が一覧形式で読むことができるので、

Spockはかなりいいですね。


Spockのサンプルに載っているようなテスト





実は僕、これの何が嬉しいのかわからなかったんですよ。


だけど、@RunWith (Theories.class)を書いた後に、


Spockで書きなおしてみると、Spockのありがたさが非常によくわかります。


真の結論











ということなので、いろふさん早くSpockについてブログ書いて下さい。








2012年12月21日金曜日

えっ!いろふさんならさっきテストコードで見かけたよ! #irof_history

これはいろふAdvent Calendar 2012の21日目の記事です。

昨日は@tan_go238さんの『irofコマンドをbrewでインストールしてみる 』でした。

明日は@zer0_uさんの『ぬくもりろふ #irof_history』になります。


いろふさんはいろんなところにいる所で有名ですが…


本人のブログを見ると、結構テストにいることが多いみたいです。

で、僕も先ほどテストで見たんですよ。





実装は単純です。


単に比べているものがequalsで等しいか、

nullでないかだけで結果を返しています。





TODO


このライブラリーしょぼいので以下の実装を予定しています。

  • いろふさんのツイートが二時間ない場合はfailになる
  • @Irofアノテーションを付与すると、すべてのクラスにirofメソッドが作成される(Groovyのみ)
  • Twitterと連携して落ちたテストについてツイートする

以下は開発中の画面です。

2012年12月20日木曜日

IPA 平成24年度 システムアーキテクト試験 午後2 問1 解答例 with TDD

このエントリーは『TDD Advent Calendar 2012』の19日目のエントリーになります。

18日は@haru01さんの「TDD の素振りをしよう – haru01のめも」でした。


予告だと…


コマンドプロンプトとか言ってたな。

アレは、僕が冬眠中でネタを仕込むのに時間がかかるので、

やめたわ…


テーマ


TDDに特に思い入れがないので(失礼)、

なんか現場でこういうふうに導入しようという考えをいろいろと書いてみましたが、

まとまりにかけて、これも没にしました。


そこで


IPAの情報処理技術者試験のシステムアーキテクト試験午後2の問1の模範解答を

TDDをテーマに書いてみることにしました。

ちなみに、まだ途中までしか書いていません…


問題はこんな感じです。


業務の変化を見込んだソフトウェア構造の設計について

 企業を取り巻く環境の変化に応じて、業務も変化する。情報システムには、業務の変化に対応して容易に機能を変更できるような、ソフトウェア構造の柔軟性が求められる。
このため、システムアーキテクトは、システム要件定義の段階から、業務の変化が起こり得るケースを想定し、変化の方向性やシステムに与える影響を予測する。ソフトウェア構造の設計では、その予測に基づいて、業務が変化してもシステム全体を大きく作り直す必要がないように考慮しなければならない。
例えば、次のようにソフトウェア構造の設計を行う。

  • 業務フローの制御部分と業務ロジック部分を分離する。
  • 業務ロジックが互いに疎結合となるように分割する。
  • データアクセスコンポーネントを共通化する。

 その際、そのような設計を行うことによって引換えに生じた課題に対応するための工夫を行うことが重要である。例えば、処理時間が長くならないように複数のプロセスを並行して処理したり、処理同士の整合性を確保するために排他制御の仕組みを用意したりする。
あなたの経験と考えに基づいて、設問ア~ウに従って論述せよ。


設問ア あなたがソフトウェア構造の設計に携わったシステムにおける、対象業務の概要及び特徴について、800字以内で述べよ。

設問イ 設問アで述べたシステムについて、どのような業務の変化を想定したか。また、業務が変化してもシステム全体を大きく作り直す必要がないように、どのようなソフトウェア構造を設計したか。800字以上1,600字以内で具体的に述べよ。

設問ウ 設問イで述べたソフトウェア構造の設計において、生じた課題とそれに対応するために重要と考えて工夫した内容、及び設計したソフトウェア構造に対するシステムアーキテクトとしての評価について、600字以上1,200字以内で具体的に述べよ。

(IPA 独立行政法人 情報処理推進機構より引用)


僕が書いた模範回答


設問ア
私は都内のシステムインテグレーター(以下A社と省略する)に勤務するアプリケーションエンジニアである。A社の顧客に大手製造業のZ社がある。Z社は国内および海外に製造・販売の拠点を持つグローバル企業である。本論文ではA社が昨年請け負ったZ社の生産支援システム(以下、生産システムと省略する)について述べていく。
Z社はかねてより海外展開している企業であった。しかし、20 08年以降日本国内での流通量が激減しており、勃興するアジア市場でいかに売上を伸ばしていくかが課題となっていた。Z社で従来から旧生産システムを用いていたが、システム保守にかかる時間が長期化しており、変化が激しいアジア市場に対応するには厳しかった。そのため、Z社では激しい市場の変化に柔軟に対応できる新生産システムを企画し、A社が受注することになった。







2012年7月22日日曜日

TDD in Actionに参加してきた

涼しいですね。体調が悪くて死にそうです。

みけです。

JavaScriptで参加してきた


最近はJavaとかGroovyは毎日やっているので、

たまには違うものをやりたかったので、

JavaScript + qunitでの参加となりました。


ペアプロ


ペアを組んでやりましょうということで、

@tomy_kairaさんと組んでやりました。

トミー君はemacs的な人で、僕はWebStormなので、githubを介してTDDしてました。

僕はここ二日間の調子が悪くて、頭がどっか行っていたので、

「あー、いいねー…あー、うー」という感じでしたので、

トミー君の頭の回転には脱帽という感じです。


成果物


途中までできました。

github上にあるので、まあ見てみて下さい。

mike-neck/Tdd-In-Action


qunit


今回使ったのはqunitですが、

まだ使い始めたばっかりだったので、

うまく使いこなせていない感が半端無かったです。

ここで、実現できている機能とかは、Fx-Js-JUnitでは最低限実現したいところではあります。


Erlang


後半のほうでgdgdな時間が発生していたので、

一人でErlangの勉強していました。

こっちは大した成果はありません…

2012年7月10日火曜日

その設計は適切ですか?

大分、咳が収まりつつあります。

みけです。

今週はネガティブになってしまう週(二週間を周期とする気分の波があります)なので、

ネガティブなことをいろいろと書き殴りたい気分ではあるんですが、

思ったことを少しだけ書きます。


共通化関数


まあ、ツイッターで結構出回っているので、

皆さんすでに読まれているかと思います。

日々常々 - 誤った共通化

あのKBNという変数名、嫌ですね。

なんとかならないかと思います。


まあ、そんなことを言っている当の私も数年前はKBNであったし、

エクセル方眼氏をこよなく愛していたわけですが…

指摘されているような過ちを平気でやっていたのですが…


最近だと同じような状況だと、

次のような設計・実装にすることが多いです。


public class Printer {
    private java.io.Writer writer;

    public void setWriter (java.io.Writer writer) {
        this.writer = writer;
    }

    public void writetGreeting (Language language) {
        writer.write (language.getGreeting());
    }
}

public interface Language {
    public String getGreeting();
}

public enum AncientLanguages implements Language {
    LATIN {
        @Override public String getGreeting () { return "Lorem"; }
    },
    GREEK {
        @Override public String getGreeting () { return "Γεια σας"; }
    }

    @Override public String getGreeting();
}

public class LanguageService {
    public void writeGreeting (Writer writer, String language) throws IllegalArgumentException {
        Printer printer = new Printer();
        printer.setWriter(writer);

        Language lang = AncientLanguages.valueOf (AncientLanguages.class, language);
        printer.writeGreeting (lang);
    }
}

enumを一つの状態を表すので、それに何かをさせるのがあるべき姿かと

考えています。なので、enumにメソッドを実装させることが多いです。



設計の適切度の指針としてテストを利用する


上記の例で言えば、比較的テストは簡単に書けます。

public class PrinterTest {
    @Test public void testGreeting {
        StringWriter writer = new StringWriter ("test-");

        Printer printer = new Printer ();
        printer.setWriter(writer);

        Language language = new MockLanguage ();
        printer.writeGreeting(language);

        assertThat(writer.toString(), is("test-hoge");
    }

    private class MockLanguage implements Language {
        @Override public String getGreeting () { return "hoge"; }
    }
}

こんな感じで、モッククラスを二つ準備するだけで、テストが書けます。
  • Writer→StringWriter
  • Language→MockLanguage


しかし、このPrinterクラスが依存するクラスが多かった場合、どうなるでしょうか?

おそらくテストコードは読みづらいものになっていたと思います。

例えば、こんな感じ。




モックするクラスが多いクラスというのは、

一つのクラスでいろんなことをやろうとしているクラスです。

つまり、責務分割がしっかりなされていない設計になっているといえます。


クラスに保持するフィールドは三つ以下にする


これは『プロダクティブプログラマー』に書いてあった

内容です。(間違えているかもしれません)


もし、フィールドがそれ以上増えるようでしたら、

そのクラスの責務が多すぎるようなので、

再設計を検討して下さい。


以下、参考書籍。





読んだことありませんし、持っていませんが…


読みづらいテストコードの元例の書いてあった本。
ちなみにモックの数が多い場合は、責務が大きくなっているので再分割することを検討して下さいとも書いてあった…

2012年6月29日金曜日

JavaとJUnit雑感

こんちは。

みけです。

昨日はいろんなもののリリースというか発表というかありましたね。

2012年の半分も過ぎたわけで、成果発表という感じでしょうか。

アノテーション


僕はまあ、SIerにでっぷり浸かって、目先の仕事に追われていた時期が

長かったため、最近になってやっとプログラムに目覚めた感じの

残念な中年のおっさんなわけですが、

アノテーションとかAPT、PAP-APIとかを知ったあたりで、

結構衝撃があったわけですね。

Javaでメタ情報を扱えると。


JUnit


で、その衝撃を持ったまま、

Androidテスト部の勉強会に参加して、

「テストの自動化?そんなの普通だよ」という人達の

お話を聞いたものですから、

なんとなくの勘というか直感だったのですが、

「アノテーションでJUnit回せるんじゃね?」という

妙な自信みたいなものが生まれてきました。


勘


人間の直感というのは時に熟考よりも的確な場合があるもので、

昨日(2012/06/28)のtry-with-resourcesの記事を書いていた時に、

テスト条件をenumで回す実装を書いてて、

「アノテーションとenumをPAP-APIから使えば、

コンパイル時にテストできそうだな」という確信に似たものを

なんか持ったようです。


どうなんだろう、ちょっとやってみたい気もするなぁ…

2012年6月28日木曜日

Java 7 の try-with-resources をさわってみる

こんにちわ。みけです。

Java6のEOLが今年(2012年)の11月に迫っています。

なので、Java6を使っている人はJava7に切り替え始めましょう。

Javaの最新7の5分の1のバージョンを使っている人(つまりJava1.4を使っている人)はとっととアップデートして下さい。

Java1.4のEOLは2008年10月30日です。

Sunと契約している場合を除いて、契約していない人たちは泣き言を言わずアップデートしましょう。


…以前も同じ事書いた記憶が…

本題


昨日(2012/06/27) java-jaの『Log.debug("nice catch!")』に行って来ました。

開催者のよしおりさん、やましろさん、いつもご苦労さまです。

まあ、ツイッターとかまとめられているので、当日の様子については

そっちを御覧ください。しんやさん本当ご苦労さまです。

で、例外ハンドリングということで、Java7の例外ハンドリング、

try-with-resourcesが話題になったので、少し調べてみました。


ggrks


まあ、調べるったって、ggrだけなんですが、

たくさんエントリーありますね(白目。

なので、詳しい話はそっちを見て下さい。


そうは言っても


僕は自分の指を動かしたことしか理解できない単純な構造をしているので、

他のブログのエントリーと同様にコードを書いてみました。



まあ、テストの形をとっているのが他のブログと少しだけ違うかな。

で、テスト条件は次のWhen.javaというenumです。



enumにメソッドが実装できるのは周知の話で、

今回はenumの各値に条件を返すような実装を与えました。

  • onConstructorメソッドはコンストラクターで例外が発生する・しないを表します。
  • onMethodメソッドはメソッドの途中で例外が発生する・しないを表します。
  • onCloseメソッドはjava.lang.AutoCloseableで定義されているvoid close()メソッドで例外が発生する・しないを表します。

まあ、コード見ればわかりますが、

テスト条件を日本語でメモっとくとこんな感じ。

  • OnConstructorOnly
    • コンストラクター : 発生する
    • メソッド : 発生しない
    • クローズ : 発生しない
  • OnConstructorWithClose
    • コンストラクター : 発生する
    • メソッド : 発生しない
    • クローズ : 発生する
  • OnMethodOnly
    • コンストラクター : 発生しない
    • メソッド : 発生する
    • クローズ : 発生しない
  • OnMethodWithClose
    • コンストラクター : 発生しない
    • メソッド : 発生する
    • クローズ : 発生する
  • OnCloseOnly
    • コンストラクター : 発生しない
    • メソッド : 発生しない
    • クローズ : 発生する
  • NoException
    • コンストラクター : 発生しない
    • メソッド : 発生しない
    • クローズ : 発生しない

そうそう、これやってて気づいたんですが、

enum使えば、デシジョンテーブル系のテストをすんなり記述できますね。

まあ、enumをテスト対象に挟みこむにはインターフェースを使わないと難しいですが…


テスト実行


テスト実行しました。

コンソール出力は次のとおりです。



これをみててわかるのはclose()メソッド中に例外が発生した場合、

  • 例外発生後に例外が発生した場合は握りつぶす!
  • 正常に終了しているときに例外が発生した場合は例外を通知する

という動きをしていますね。

まあ、リソース処理系の例外の処理ってだいたいそうですけどね…


ロギング


ちなみにSlf4jでログを出力していますが、

ログ実装はLog4jです(´・ω・`)

なお、ログ実装については



ってことで、Markerについても覚えておきたいですね。

ちなみに日本語で最も正しいMarkerの使い方は

たいちさんの『設計と実装の狭間』でだそうです。



2012年6月27日水曜日

JavaFXのApplication Threadと戯れる-その2

ニャル子さんが終わったので、アイコンを元に戻しました。

みけです。

スレッド周り


Fx-Js-JUnitの話で少しだけ触れましたが、

JavaFXアプリケーションはスレッド周りが大変です。

単品のJavaFXアプリケーションを作る分には、

それほど問題はありませんが、

JUnitと合体させたものを作ろうとすると、

スレッドに関する知識がないと

マジで難しくなります。

死ねます。

死なないで下さい。


プログラムが処理されていく順番をしっかり覚える


というわけで、マルチスレッドなプログラムの処理順を

しっかり抑えておくことが大切です。

というわけで、アプリケーションの起動から終了に至るまでの

順番をログに出力するサンプルコードを書いてみました。



これは単純にアプリケーションを起動して、

終了するだけのコードです。


クイズ


さて、ここで問題です。

Application.launch(App)の後にある

アプリ起動したというログが出力されるのは何番目でしょうか?


宣伝


7月2日に@skrbさん主催の

『第 7 回 JavaFX 勉強会 ツール特集』にてLTやります。

ユーストもあります。

ぜひお楽しみに!


答え


実行した結果を以下に示します。



アプリ起動したは、アプリ終わっちゃうの?の後に着ていますね。

要するにApplication.launch(App)の後はスレッドは残ったまま、

アプリケーションの終了を待機してしまいます。

したがって、「アプリを起動して、それから何かの操作をアプリに対して実行して」

というシナリオでテストを書く場合には、

必ず別スレッドでアプリケーションを起動する

ようにしましょう。

2012年6月12日火曜日

Fx-Js-JUnitの改良1



誰か、Retina MBP僕に下さい。

みけです。

今のまずい点


まず、まったくもっていただけないのが、Fx-Js-JUnitがエンベデッドのサーバーしか想定していないところ。

昨日のテストコードで@ClassRuleアノテーションを付与した箇所を見てみましょう。

    @ClassRule
    static public UseFxWebView fxWebView = UseFxWebView
                                                .defaultServer()
                                                .identifiedBy('JsJUnitTest')
                                                .get()

このUseFxWebViewというクラスが実はエンベデッドサーバーに依存しているのです。





ここはもうすこしちゃんとビルダーの設計をしてFx-Js-JUnitのコア(JavaFXでJavascriptのコードをJavaから呼び出す)部分とFx-Js-JUnitのエンベデッドサーバー(自プロジェクトのJavascriptを読み込めるようにする)を分離したいと思っています。

2012年6月11日月曜日

JavaFX + JUnit で javascriptのunit testできるようにしてやるんで、これからハマっていってやんよ - 4

前回のポストから約2ヶ月半、やっとできましたよFxJsJUnit。

みけです。


ひらめき


JavaFX2.0の発表を聞いた時にWebViewがwebkitを搭載するということで、

すぐにjavascriptのテストをJavaで書けるようになると閃いて、

今年の3月くらいにとりかかりました。

桜庭さんからJavaからJavascriptを呼び出すにはjavafx.scene.web.WebEngine#executeScript(java.lang.String)を叩けばよいと聞いていたので、

実際、初回に叩いてみたわけですが

JavaFXのコンポーネントはJavaFXスレッドで立ち上げないとダメということで、挫折しました。


成功?!


その後、桜庭さんのブログエントリーで色々とアドバイスを頂いたりしました。


それを参考にしてコードを書いてみたら、テスト1つは通ったのですが、複数回のテストが実行できないという残念な結果に終わりました。


スレッド、スレッド、スレッド


もともと業務SEさんで、スレッドとか興味なかったのでスレッドの制御に悩みました。

3月末頃にはコードがグダグダになってきていて、どうしようもなくなっていたようです。

この頃は何に悩んでいたかというと、

  • テストを起動→Webサーバー起動→JavaFXアプリケーションを起動という順番で実行
  • JavaFXアプリケーションが起動完了したところで、何も動かなくなる

といった状態でどのスレッドで何がどうなっているかが全く?になっていました。

JUnitとWebサーバーとJavaFXアプリケーションとWebViewと4つのスレッドの同期化を図りつつ実行していかなければならないので、

スレッドの知識がない僕には何がどうなっているのか全くわからない状態でした。


Java並行処理プログラミング


そこで、スレッドで悩まないために、Java並行処理プログラミング ―その「基盤」と「最新API」を究める―を読みました。



桜庭さんの記事にもあるように4つのスレッドの制御のためにはjava.util.concurrent.BlockingQueue>T<や、java.util.concurrent.ExecutorServiceを押さえとかないと厳しいです。

僕もこの本のお陰でBlockingQueueとかがなんとなくわかるような気がしてきました。

で、やっとうまく行った実装では次のようにスレッドを作っています。


これらのスレッドにBlockingQueueで値を受け渡しさせることで同期化を図っています。


テストコード


テストコードを書く人(ユーザー)には、これらのスレッドのあたりを意識させない(隠蔽して)ようにするのが望ましい形です。

そこで、JUnitの@ClassRuleでテストクラスの実行前に準備することで、

ユーザーにはjavascriptの呼び出しだけを提供できるようにしました。

サンプルのテストコードは次のとおりです。

JsJUnitTest.groovy



コードはGroovyで書いていますが、Javaに近い形で書いていますので、

それほど読みづらくないと思います。


テスト対象のJavascriptは次のとおりです。

test.js




技術的なこと


実は苦労話ばかり書いていて、技術的なことは何も触れてないですね。

そのあたりは、7/2(月)にある桜庭さん主催の『第7回 JavaFX 勉強会』でお話しようと思います。

まあ、JavaFXの話は殆どなしで、javascriptとスレッドの話になりそうですが…


TODO


一応スタティックなWebサーバーのjavascriptのテストが実行できるようになったわけですが、

まだまだ、javascriptのJavaにおける扱いに関して不明な点が多くありますし、全然型安全でありません。

また、ajaxなどのテストも書けませんし、DOMでassertする部分のサポートも不十分です。

さらにGitHubリポジトリーも作ってないですし、今のgradleスクリプトではMac以外では動きません( ー`дー´)キリッ

というわけで、次のような課題があります。

  • Windows/Linux対応
  • リポジトリ公開
  • javascript→POJOマッピング対応
  • function型の扱い
  • ServletコンテナもしくはJavaEEコンテナ搭載

このあたりはボチボチとやっていきたいと思います。

2012年4月8日日曜日

#なごやこわい #うさみみ NagoyaTestingに参加してきた。

将来、娘の名前には「姫」と名付けたいみけです。

結婚する予定も付き合っているお姉様もおりません。

参加するきっかけ


なんかツイートが回ってきたから。

ちなみに参加したイベントはこれ。

Nagoya.Testing in Tokyo


後付で、上流テストをよく知らないとか言ったけど、

本当は無職になることがわかっていて、

どうせ暇だから参加すると決定した次第です。


Nagoya.Testingとは


うさみみこと @kyon_mm が自説を長々としゃべるオフ会上流テストについて詳しく説明し、実際に手を動かしてテストをいかに作るかを学ぶ勉強会です。

なお、当日の資料については公開されています。



実習


おおたけさん、おおわしさん、紅千鳥さんと同じチームでした。

実習内容はテストを設計し、実装し、実施報告をするというもの。

みんな優秀で(、とくにおおわしさん)、関心してしまいました。

そんなわけで、テスト実施までこじつけることが出来ましたが、境界値とかの設計が足りなかったのと、異常系のテストがなかったかな。

比較的コンパクトなテスト設計ができて、それはいいところだと言われました。

相手のいいところを探して、さらにこうすればいいと説明するうさみみ、素敵です。でも、こわくなくなるともっと(ry。


感想


勉強会に参加したことのブログを書くのは、感想を読者の皆さんと共有して何かを得てもらうというのと同時に、自分にとっても何らかの感想を自分の次のステップへ導くための礎とするものだと思いまうす。

  • 勉強会の形式について
    • 聴講型の勉強会は成功事例などを通して、勉強へのモティベーションを上げる
    • ハンズオン形式の勉強会は実際に作業を通して勉強することで、座学では得られないスキルアップを体験する
  • ハンズオン形式の勉強会について
    • 積極的な発表をすることによって、作業内容を振り返ることができるので、発表の機会があったら積極的に挑戦してみる
    • わからないことは積極的に質問する。これによって、テキスト等からは得られないより具体的なアドバイスを得られる
  • 商品の品質は計画的に作る
    • 顧客からの要求は曖昧なので、具体的な品質に再定義する
    • WF型でやると品質がおざなりになるので、早い段階からテスト計画を立てる
    • ある程度設計が完了してくると工数の見積が可能になってくる
  • JSTQB関連の勉強はしておいたほうが良い
    • 開発というのは方法論が盛んに議論されていて、成熟したものである一方、テストはまだまだプロセスとして未成熟な領域である。
    • ある程度体系化したJSTQBに依拠することで先ずは標準的なプロセスを覚える
    • プロジェクトに応じてJSTQBのフレームを活用することで、プロセスモデルのテストを実施して、よりテストプロセスを強化する。
    • 情報を発信する

私が気になったところはこのようなところでしょうか。

あと、やはり開発者なので、今回のテストの勉強はひさびさな感じで、テスト力の衰えを感じました。

こういう勉強会は定期的に開かれると良いですね。

2012年3月25日日曜日

JavaFX + JUnit で javascriptのunit testできるようにしてやるんで、これからハマっていってやんよ - 3

ども。

あいかわらず、ハマっています。

現状のコード


かなりひどいことになっています。





とにかく、ページをロードした時の同期を図るのがかなりしんどいです。

JUnit / JavaFX / WebView 3つのスレッドを管理しないといけないので、

これがかなりきつい。

ExecutorServiceを使うといいのでは?

と、java-ja温泉でよしおりさんに教えてもらったので、

改善していこうと思います。


2012年3月16日金曜日

最近勇気づけられた言葉 - The word encouraged me recently.

@snskさんの言葉です。


ガチに超すごいプログラマーになるのには才能が必要だけど、テスターになるためには努力が重要。


子供の頃からベーシックを触ってプログラムの基本的な制御構造などは理解していたけど、

それほどのめり込まなかった私は、多分、スーパープログラマーとして生きるには、

才能の片鱗もないのかなと思っています。

そんな中、デブサミの打ち上げで@snskさんのこの言葉、

非常に励みになります。

だから、私、テストエンジニアーでもいいと思っているんです。


思想的に


私が比較的考えが近いなと思っている思想家が何人か居ます。

クルト・ゲーデルが結構近い考えを持っていると勝手に認識しているのですが、

彼の基本的なスタンスは他の誰かのアイデアを徹底的に自分のものとし、

そのアイデアを徹底することでアイデアの単一矛盾点を明らかにする事。

ジャック・デリダなんかもそうですね。

テストも同じだと思っていて、仕様・設計を徹底的に継承することで、

仕様・設計に内在する不可避なものを明るみに出すという、

そのようなテストができるようになりたいと思うことがあるわけですよ。

まあ、嫌がられますけどね。

2012年3月9日金曜日

JavaFX + JUnit で javascriptのunit testできるようにしてやるんで、これからハマっていってやんよ - 2

どーも、季節性鬱症がかなりひどくてやる気が0なミケです。


ツイッターでJavaFXの同期がむずいとつぶやいていたら、@skrbさんから



こんなツイートをいただきました。

JavaFXとJUnitのThreadについて丁寧に解説されていて、動作できたようです。

というわけで、コピペプログラマーとしてはコピペしないわけにはいきません。

早速Jettyも使ってunit testできるようにしましょう。


@BeforeClassで起動するWebサービス


JUnitのテストコードの中でServerを持っているのが大分辛くなってきたので、

Serverのコードは外に出すことにしました。

まだ、試作段階なので、Handlerクラスなども固定でしか動きません。




一応、サーバーのアドレスとかポートも設定できるようにしておきたいので、

コンストラクターで指定できるようにはしてあります。

ただし、現段階では使っていません。


TestBrowser


JavaFXのWebEngineを使ってテストコードを走らせるクラスです。

@skrbさんのブログの内容をそのままコピーしています。

なお、一部分を変更しています。

どうやら@Testメソッドを実行時に、画面のロードが完了していないなどの同期の部分で

失敗したため、javascript上でロードしたことをマークするようにして、ロードが完了するまでは待機するように

改造してあります。




なかなか、ソースが読みづらくてごめんなさい。試作段階ということで許しておくれ。

Threadの同期と非同期は難しいですね。

@skrbさんには非常に感謝しております。



実際のテストコード


実際のテストコードです。

これは単純に数値を計算するfunctionを呼び出しているだけです。




なお、@BeforeにてApplication#launch(Class<T extends Application>, java.lang.String[])の

第二引数でサーバーのURLを渡してあります。

これは、アプリケーション側でgetParameter()メソッドを使うと取得することができます。

サーバーのポートなどの問題でURLを変更することはよくあるので、

この辺は可変にしておきたかったです。



実行結果


実行結果は次のような感じになります。



お気づきな方もいると思いますが、doStringTestの方は@Ignoreしています。

これ、@Ignoreを外すと、2つ目のテストが今は動かない状態なんです。



これはTestBrowserの部分の作りがまだまだ粗いため、2つ目のテストが実行できない状態になっているからですね。

TestBrowserの起動を@BeforeClassに移動して、

@Before毎にページを読み直すような仕様に変更しようと考えています。


とりあえず、今はこんな感じです。


どうやら…


@skrbさんの周囲で、

JRubyのなひさんとJenkinsの川口さんといった

錚々たる面々のお方から反応があったらしく、

意外と面白そうなプロダクトになりそうな気がしてきました。


Seleniumと比べて


SeleniumなどのUIテスト系と比べて、僕がこのFxJsJUnitでやりたかったのはjavascriptのunit testなので、

実はUIはあまり気にしていないんです。

なので、縦幅がいくつとかはあまり興味がなくて、

純粋にjavascriptの関数に対してTDDを実施していけるような状態にしたいというのが第一の目標です。

あと@skrbさんのブログにあったとおり、

GUIは別に表示しなくてもいいという点からいくと、

Seleniumのように画面がポコポコ生まれなくていいというのが強みかなと思っていたりします。

あとはwebkit搭載なので、巷に最近溢れているブラウザー(Chrome/Android用のブラウザ/Safari)に対応できるので、

モバイル系のアプリにも対応できるテストツールになりうるかもしれません。


いずれにせよ、もう少し安定して動かせるようにしたいところです。


なお、コードはgist( https://gist.github.com/2001562 )上にあげていますので、ぜひご意見等いただければ幸いです。

あと、そのうちちゃんとしたプロジェクトとしてgithubにプロジェクトを作りたいと思っています。







2012年3月6日火曜日

JavaFX + JUnit で javascriptのunit testできるようにしてやるんで、これからハマっていってやんよ - 1

こんちわ。

大分暖かくなって来ました。

暖かくなってくると、旅に出たくなります。

特に、春なら鹿がお勧めです。

ついでに勉強会にも参加しましょう。

というわけで、


鹿駆動勉強会



とかいうのにエントリーすることになりました。

能楽堂でLTをするらしいです。


で、ネタ


JavaFX1のときはあまり惹かれなかったんですが、

JavaFX2になって、Webkit搭載となったので、

おお、JUnitでjavascriptのテストできんじゃね~と思ったので、

試してみることにしました。

まあ、その辺の経緯はtogetterにまとめてます。


事前にJavaFXとThreadにハマった言い訳を書いておく


もともと業務SEだったオレなので、

JavaのGUIとかはあまり触ったことがないというか、

ほとんど触ったことがないというか、

Javaを勉強した時にチョコっと触っただけです。はい。

なので、かなりクソいコードになっています。

サーバーサイドのJavaは書いたことありますです。



テスト対象のJavascript




特に大したことのないコードです。

数字を返す関数、文字列を加工して返す関数、オブジェクトを返す関数です。



テスト戦略???


テスト対象のJavascriptは大したことのないものですが、

ajaxアプリケーションのテストもやっていきたいので、

サーバーを立てていこうと思います。

JUnitでテストサーバーを立てる場合、

以前のエントリにも書きましたが、

Jettyエンベデッドサーバーを用いていきます。

サーバーが立ち上がった後で、ブラウザを立ち上げて、javascriptのテストをしていくという流れになります。

したがって、書いていくJUnitのコードは次のようになります。

  • @BeforeClassにてJettyサーバーを立ち上げる。
  • @Beforeにてブラウザーを立ち上げる。
  • @TestにてJavascriptのテストを実行する。
  • @Afterにてブラウザーを終了させる。
  • @AfterClassにてJettyサーバーを終了する。


JUnitのコード


次のような感じです。




なお、ハンドラーをJUnitに書くと若干読みづらくなるので、ハンドラーは別クラスにしてあります。





ハマる様子をとくと見よ


さて、JsJUnitを実行してみるとこんな感じになる。





JavaFXのWebViewはJavaFXのThreadで立ち上げなければならないらしいだって(´・ω・`)



さて、まだまだハマるのだが、それは後日。

2012年2月13日月曜日

AndroidのUnit Testをするなら読んでおきたいコード

単なるリンクメモ


Activityのテスト関連

  • android.test.ActivityInstrumentationTestCase2<T>
    • Activityのテストを書く時に継承するクラス。有無を言わず読んどけ。
  • android.test.ActivityTestCase
    • ActivityInstrumentationTestCase2<T>が継承しているクラス。とりあえず、すこしだけ読んどいたほうがいいかも。
  • InstrumentationTestCase
    • ActivityInstrumentationTestCase2<T>が最も依存しているクラス。より高度な知識を得たい時には読んどいたほうがよい。
  • android.test.InstrumentationTestRunner
    • テストを起動するActivityと同等のもの。もしJUnitと同等のレポートを出力する場合には、このクラスを改造するので読んでおいたほうがよい。
  • android.app.Instrumentation
    • InstrumentationTestRunnerの基底クラス。JUnitと同等のレポート出力するために読んでおきたい。



2012年2月3日金曜日

JavaFX初心者がJavaFXに挑戦してみた

JavaFXに興味はなかったんですが…

javascriptのテストをJUnitから実行できるんでね?


こんなことをつい言ってみてしまったので、まあJavaFX触ってみることにしました。

準備


  • Java 1.7.0_02
  • IntelliJ IDEA … version10です。すみません。
  • Maven3.0.4
  • JavaFX2.0


とりあえず、Java7使っています。Java1.6系でも動くらしいです。

家でのコーディングにはほとんどeclipseを使いません。IntelliJです。

試しにmvnコマンド叩いたら、バージョンが2.2.1というひどい状態だったので、最新の3.0.4を入れました。

JavaFX2.0インストールした記憶がないのに、インストーラーを起動するとすでにインストールされている旨エラーメッセージが表示されて、なんでだろうとC:\Program Files\Javaの中を漁っていましたが、結局、見つからず(´・ω・`)して、32bit版をダウンロードしてインストールし…

っていう時に、インストール先がC:\Program Files\Oracleということを知り、探したらありました。


artifactId…


さて、ビルド周りをきっちりやりたいので、mavenでプロジェクトを作ります。

単純にIntelliJでCreate new Project from Scratchして、maven moduleを選択しただけですが…

さて、javafxもmavenからライブラリーを落としてこよっと思ってmavenrepositoryを検索したら残念なコトにartifactIdがございませんでした。

さて、こういう場合はローカルにあるjarをローカルリポジトリーに登録するらしいです。
特にOracleのプロダクトに関してそういうことが多いようです。


というわけで我々もやってみました。


C:\>workspace\JavaFxWebView > mvn install:install-file -Dfile=jfxrt.jar -DgroupId=javafx -DartifactId=javafx -Dversion=2.0 -Dpackaging=jar


それをpom.xmlに指定して、ってな感じでやると見事!プロジェクトに取り込まれました。


pom.xml




2012/02/04 2:50 修正


DLL地獄!?


ここを参考に超シンプルな実装をしてみました。





Creative Commons License
MikeBrowser by Shinya Mochida a.k.a. mike_neck is licensed under a Creative Commons Attribution 3.0 Unported License.

(∩´∀`)∩ワーイということで、早速コンパイル。


C:\>workspace\JavaFxWebView > mvn clean compile




コンパイル通りました。

では早速実行しましょう。


C:\>workspace\JavaFxWebView > mvn exec:java -Dexec.mainClass="org.mikeneck.jfx.MikeBrowser"




Σ(゚д゚lll)ガーン落ちたー。



C:\Users\mike\.m2\repository\javafx\javafx\bin\mat.dll



mat.dllがないらしい。

まあでもブラウザーを持っていたりするんだから、そうなるよね。

さて、このmat.dllくんはどこにいるのかな?

いた!


C:\Program Files\Oracle\JavaFX 2.0 SDK\rt\binにいるそうです。

残念、これは手でローカルリポジトリーに上げるしかなさそうです。

ということで、手で突っ込んでみた。


では、気をとりなおして、再実行!

やりました!出てくれました!






2012年1月14日土曜日

Grails2.0のDomainをTDDしてみる。

出展はいつもどおりの『Grails徹底入門』
の96ページにあるモデル図から。

ここから、今回の対象部分のモデルを抜き出したのが以下の図。
で、今回はこのうち、Shipmentの部分をTDDしていきます。


ドメインの作成


ドメインの作成はいたって簡単です。


$ grails create-domain-class Shipment


これだけで、Shipmentドメインクラスと、ShipmentTestsテストクラスが生成されます。

Shipment.groovy

package grailsshop

class Shipment {

    static constraints = {
    }
}


Shipment.groovy

package grailsshop
import grails.test.mixin.*
import org.junit.*

@TestFor(Shipment)
class ShipmentTests {

    void testSomething() {
        fail()
    }
}


なお、テストクラスはデフォルトではfailになるようになっています。

最初のテスト


Shipment(出荷)はイベントなので、必ず日付をもちます。したがって、nullは禁止です。

それをテストに書きます。

なお、Grails2.0はJUnit4対応しているので、アノテーションを用いることでテストメソッドであることを示せます。

ここではテストメソッド名も変更しています。

また、validateのテストになりますので、前回の結論に書いておいたようにmockForConstraintsTestsメソッドを最初に用いておきます。

Shipment.groovy

package grailsshop
import grails.test.mixin.*
import org.junit.*

@TestFor(Shipment)
class ShipmentTests {

    @Test
    void validateDate() {
        mockForConstraintsTest(Shipment)
        def object = 'date'

        def shipment = new Shipment()
        assert shipment.validate() == false
        assert shipment.errors[object] == 'nullable'
    }
}


実行結果は次のようになります。


grails> test-app grailsshop.Shipment
| Running 1 unit test... 1 of 1
| Failure:  validateDate(grailsshop.ShipmentTests)
|  Assertion failed:

assert shipment.validate() == false
       |        |          |
       |        true       false
       grailsshop.Shipment : null

 at grailsshop.ShipmentTests.validateDate(ShipmentTests.groovy:20)
| Completed 1 unit test, 1 failed in 1930ms
| Packaging Grails application.....
| Tests FAILED  - view reports in target/test-reports
grails>


まあ、Shipmentにはdateというフィールドをまだ実装していないので、落ちるのもやむなしです。

暗黙のnullable : false


そこで、実装に行きます。

とりあえず、ShipmentクラスにDate型のdateを持たせてみます。

Shipment.groovy

class Shipment {

    /**
     * 出荷日付.
     */
    Date date

    static constraints = {
    }
}


この状態でテストを実行してみます。


grails> test-app grailsshop.Shipment
| Completed 1 unit test, 0 failed in 152ms
| Tests PASSED - view reports in target/test-reports
grails>


というわけで、とくにnullチェックの実装を入れていませんが、テストが通りました。

Grailsのドメインクラスにあるフィールドはデフォルトでnullableがfalseのようです。

適当に制約をいれていく


Shipment(出荷)、捉え方によるけど、未来の日付は入れられないことにしておきましょう。

(出荷予定であれば話は別ですが…)

それをテストに書きます。

ShipmentTests.groovy

@TestFor(Shipment)
class ShipmentTests {

    @Test
    void validateDate() {
        mockForConstraintsTest(Shipment)
        def object = 'date'

        def shipment = new Shipment()
        assert shipment.validate() == false
        assert shipment.errors[object] == 'nullable'

        shipment = new Shipment(date: dayFromToday(1))
        assert shipment.validate() == false
        assert shipment.errors[object] == 'max'
    }
}


dayFromToday(int)は今日からの日付を取るユーティリティーメソッドです。0を指定すると今日の日付、1を指定すると明日の日付が取得できます。

翌日の日付であった場合は、エラーとなるというテストを記述しています。

で、テストの結果は次のとおりになります。


grails> test-app grailsshop.Shipment
| Running 2 unit tests... 1 of 2
| Failure:  validateDate(grailsshop.ShipmentTests)
|  Assertion failed:

assert shipment.validate() == false
       |        |          |
       |        true       false
       grailsshop.Shipment : null

 at grailsshop.ShipmentTests.validateDate(ShipmentTests.groovy:24)
| Completed 2 unit tests, 1 failed in 96ms
| Packaging Grails application.....
| Tests FAILED  - view reports in target/test-reports
grails>


まず、validate()でAssertが落ちます。

落ちた原因を探りたいので、一度テストを修正します。

ShipmentTests.groovy

@TestFor(Shipment)
class ShipmentTests {

    @Test
    void validateDate() {
        mockForConstraintsTest(Shipment)
        def object = 'date'

        def shipment = new Shipment()
        assert shipment.validate() == false
        assert shipment.errors[object] == 'nullable'

        shipment = new Shipment(date: dayFromToday(1))
        shipment.validate()
        assert shipment.errors[object] == 'max'
    }
}


テスト結果


grails> test-app grailsshop.Shipment
| Running 2 unit tests... 1 of 2
| Failure:  validateDate(grailsshop.ShipmentTests)
|  Assertion failed:

assert shipment.errors[object] == 'max'
       |        |     ||       |
       |        |     |date    false
       |        |     null
       |        org.codehaus.groovy.grails.plugins.testing.GrailsMockErrors: 0 errors
       grailsshop.Shipment : null

    at grailsshop.ShipmentTests.validateDate(ShipmentTests.groovy:25)
| Completed 2 unit tests, 1 failed in 81ms
| Packaging Grails application.....
| Tests FAILED  - view reports in target/test-reports
grails>


内容からわかるようにエラーがないということです。

ここで、正しくvalidate()でエラーとなるようにドメインクラスを修正します。

Shipment.groovy

class Shipment {

    /**
     * 出荷日付.
     */
    Date date

    static constraints = {
        date(max: new Date())
    }
}



制約を加えたらテストを実行します。


grails> test-app grailsshop.Shipment
| Completed 2 unit tests, 0 failed in 124ms
| Tests PASSED - view reports in target/test-reports
grails>


ちゃんとパスします。

念のため、今日も大丈夫か確認します。

ShipmentTests.groovy

@TestFor(Shipment)
class ShipmentTests {

    @Test
    void validateDate() {
        mockForConstraintsTest(Shipment)
        def object = 'date'

        def shipment = new Shipment()
        assert shipment.validate() == false
        assert shipment.errors[object] == 'nullable'

        shipment = new Shipment(date: dayFromToday(1))
        assert shipment.validate()
        assert shipment.errors[object] == 'max'

        shipment = new Shipment(date: dayFromToday(0))
        shipment.validate()
        assert shipment.errors[object] == null
    }
}


ポイントとしては、validate()が通るケースでは、assert shipment.validate() == trueのテストを行わない方が良いです。

理由は、これから他にもvalidateするものが増えるので、後々にテストが通らなくなるからです。

テストの実行結果は次のようになります。


grails> test-app grailsshop.Shipment
| Completed 2 unit tests, 0 failed in 81ms
| Tests PASSED - view reports in target/test-reports
grails>


リレーションに関するフィールドの追加とテスト


次にリレーションに関するフィールドの追加とテストです。

先に掲載したモデルから、ShipmentとWarehouse、Orderの関係は、次のようになります。

  • Shipment - Warehouse
    • ShipmentからみてWarehouseは唯一つ存在し、かつその参照先を保持する必要がある。
    • WarehouseからみてShipmentは0または1つ存在し、その参照先は保持しなくて良い。
  • Shipment - Order
    • ShipmentからみてOrderは唯一つ存在し、かつその参照先を保持する必要がある。
    • OrderからみてShipmentは0または1つ存在し、その参照先は保持しなくて良い。

こういうのを一般的には一対一の片方向の関連とかなんとかいうらしいです。

Grailsのドメインにおいて、これを実現するのがbelongsToです。

では、おもむろにテストを書きます。

ここでは、暗黙のnullable : falseを利用します。

まずはOrderから…

ShipmentTests.groovy

@TestFor(Shipment)
class ShipmentTests {

    @Test
    void validateOrder() {
        mockForConstraintsTests(Shipment)
        def object = 'order'

        def shipment = new Shipment()
        assert shipment.validate() == false
        assert shipment.errors[object] == 'nullable'
    }
}


テストを実行します。


grails> test-app grailsshop.Shipment
| Running 3 unit tests... 1 of 3
| Failure:  validateDate(grailsshop.ShipmentTests)
|  java.lang.NoSuchMethodError: grailsshop.Shipment.getBelongsTo()Ljava/lang/Object;
 at org.grails.datastore.mapping.reflect.ClassPropertyFetcher$GetterPropertyFetcher.get(ClassPropertyFetcher.java:326)
 at org.grails.datastore.mapping.reflect.ClassPropertyFetcher.getPropertyValueWithFetcher(ClassPropertyFetcher.java:218)
 at org.grails.datastore.mapping.reflect.ClassPropertyFetcher.getStaticPropertyValue(ClassPropertyFetcher.java:233)
 at org.grails.datastore.mapping.model.config.GormMappingConfigurationStrategy.establishRelationshipOwners(GormMappingConfigurationStrategy.java:271)
 at org.grails.datastore.mapping.model.config.GormMappingConfigurationStrategy.getOwningEntities(GormMappingConfigurationStrategy.java:716)
 at org.grails.datastore.mapping.model.AbstractPersistentEntity.initialize(AbstractPersistentEntity.java:79)
 at org.grails.datastore.mapping.model.AbstractMappingContext.addPersistentEntityInternal(AbstractMappingContext.java:150)
 at org.grails.datastore.mapping.model.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:135)
 at grails.test.mixin.domain.DomainClassUnitTestMixin.mockDomain(DomainClassUnitTestMixin.groovy:124)
 at grails.test.mixin.domain.DomainClassUnitTestMixin.mockDomain(DomainClassUnitTestMixin.groovy:120)
| Failure:  validateDate(grailsshop.ShipmentTests)
|  java.lang.NullPointerException
 at org.grails.datastore.mapping.core.DatastoreUtils.unbindSession(DatastoreUtils.java:362)
 at grails.test.mixin.domain.DomainClassUnitTestMixin.shutdownDatastoreImplementation(DomainClassUnitTestMixin.groovy:109)
| Running 3 unit tests... 2 of 3
| Failure:  validateOrder(grailsshop.ShipmentTests)
|  Assertion failed:

assert shipment.errors[object] == 'nullable'
       |        |     ||       |
       |        |     |order   false
       |        |     null
       |        org.codehaus.groovy.grails.plugins.testing.GrailsMockErrors: 1 errors
       |        Field error in object 'grailsshop.Shipment' on field 'date': rejected value [null]; codes [grailsshop.Shipment.date.nullable.error.grailsshop.Shipment.date,grailsshop.Shipment.date.nullable.error.date,grailsshop.Shipment.date.nullable.error.java.util.Date,grailsshop.Shipment.date.nullable.error,shipment.date.nullable.error.grailsshop.Shipment.date,shipment.date.nullable.error.date,shipment.date.nullable.error.java.util.Date,shipment.date.nullable.error,grailsshop.Shipment.date.nullable.grailsshop.Shipment.date,grailsshop.Shipment.date.nullable.date,grailsshop.Shipment.date.nullable.java.util.Date,grailsshop.Shipment.date.nullable,shipment.date.nullable.grailsshop.Shipment.date,shipment.date.nullable.date,shipment.date.nullable.java.util.Date,shipment.date.nullable,nullable.grailsshop.Shipment.date,nullable.date,nullable.java.util.Date,nullable]; arguments [date,class grailsshop.Shipment]; default message [Property [{0}] of class [{1}] cannot be null]
       grailsshop.Shipment : null

 at grailsshop.ShipmentTests.validateOrder(ShipmentTests.groovy:39)
| Completed 3 unit tests, 3 failed in 75ms
| Packaging Grails application.....
| Compiling 1 source files.
grails>


なんか、関係のないvalidateDateまで落ちてしまいました(´・ω・`)

このあたりはGrailsの改善に期待するしかなさそうです…

テストが通るように実装をします。

Shipment.groovy

class Shipment {

    /**
     * 出荷日付.
     */
    Date date

    static belongsTo = [
            /**
             * 発注.
             */
            order : Order
    ]

    static constraints = {
        date(max: new Date())
    }
}



実装したら、テストを実行します。


grails> test-app grailsshop.Shipment
| Completed 3 unit tests, 0 failed in 168ms
| Tests PASSED - view reports in target/test-reports
grails>


今回はすんなり通りました。何だったんでしょう?あの落ちっぷりは…



さて、Warehouseの方も同様にテスト、実装します。

ShipmentTests.groovy

@TestFor(Shipment)
class ShipmentTests {

    @Test
    void validateWarehouse() {
        mockForConstraintsTests(Shipment)
        def object = 'warehouse'

        def shipment = new Shipment()
        assert shipment.validate() == false
        assert shipment.errors[object] == 'nullable'
    }
}


Shipment.groovy

class Shipment {

    /**
     * 出荷日付.
     */
    Date date

    static belongsTo = [
            /**
             * 発注.
             */
            order : Order,

            /**
             * 倉庫.
             */
            warehouse : Warehouse
    ]

    static constraints = {
        date(max: new Date())
    }
}


結論


うむ、ほんとうは@Mockをやりたかったのだが、書いている量が半端なくなってきたので、次回に…




2012年1月7日土曜日

Grails2.0のDomainのUnit Testハマリどころ

今日はGrailsのDomainテストのハマリどころについてのメモです。
と言っても、ひとつしか書いていませんが…

ConstraintsのValidationテスト


前回の記事ではValidationのテストについて少し取り扱っています。

Book.groovy

class Book {

    String name

    String author

    int price

    static constraints = {
        name (blank: false)
        author (blank: false)
        price (min: 0)
    }
}


BookTests.groovy

@Test
    void validation() {
        def book = new Book(name: '吾輩は猫である', price: -1)
        assert book.validate() == false
    }


このテストで分かるのはこのままでは登録できないということです。

ただ、残念なことに何が悪くてValidationで引っかかったのかがわかりません。

この際に役立つのがPOGOに自動的に付与されるerrorsというメンバーです。

くせもののerrors


このerrorsにはvalidate()でチェックされたエラーのスナップショットが格納されています。

上記のコードの例では、Bookクラスには著者の情報が未設定であり、不適切な価格情報が設定されているという状態です。

したがって、errorsにはauthorとpriceというメンバーが存在します、いや、するはずです。

では、本当にpriceで引っかかっているのか確認するテストを書きます。


@Test
    void validation() {
        def book = new Book(name: '吾輩は猫である', price: -1)
        assert book.validate() == false
        assert book.errors['price'] == 'min'
    }


このテストはvalidate()でチェックされたエラーのうち、priceに対するエラーはminであることをテストします。

おもむろに実行します。

実行結果(一部略)


| Failure:  validateQuantity(grailsshop.Book)
|  Assertion failed:

assert book.errors['price'] == 'min'
       |   |      |        |
       |   |      |        false
       |   |      Field error in object 'grailsshop.Book' on field 'price': rejected value [0];
       |   Field error in object 'grailsshop.Book' on field 'price': rejected value [0];
       grailsshop.Book: null


実際はもっと長ったらしいエラーメッセージが出ますが、Field errorの部分の内容を見るかぎりではこのテストは通ってもよさそうなものなのですが、ダメなんですね。

書き方間違えたのかと思って、公式サイトを確認しましたが、合っているようです。

mockForConstraints


そんなこんなで、混乱していたところ、Grailsの第一人者であられる@tyamaさんからアドバイスをいただきました。


ふむ、mockForConstraints(Class<T>)を使えばよいとな。

というわけで、おもむろに入れてテストを通してみる。


@Test
    void validation() {
        mockForConstraintsTests(Book)
        def book = new Book(name: '吾輩は猫である', price: -1)
        assert book.validate() == false
        assert book.errors['price'] == 'min'
    }


そして、テスト実行。


grails> test-app grailsshop.Book
| Completed 1 unit tests, 0 failed in 361ms
| Tests PASSED - view reports in target/test-reports


おお、通った。

結論


とりあえず、Grails2.0でconstraintsのValidationテストをする場合は、黙ってmockForConstraintsTestsを使っとけという話ですな。


2011年10月16日日曜日

第4回Jenkins勉強会に行って来ました

第4回Jenkins勉強会に行って来ました。



さて、内容の詳細ですが、
ブログまとめ職人の@shinyaa31さんが素晴らしいものを書いていますので、
そっち読んでください。

あと、トゥギャッターもまとめられているので、読んでみてください。

togetter - 10月15日 第4回Jenkins勉強会(東京都)
2011/10/15_第4回Jenkins勉強会( #jenkinsstudy )

当日のUST録画もありますので、興味のある方は御覧ください。

以上、報告終わり!


(´・ω・`)