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

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月10日月曜日

CDIについて書こうとしたら、JSRを読み始めて、結果JSR330を実装してみようとしてみた件について

Advent Calendarをダブルブッキングしたミケです。

こっちはJavaEE Advent Calendarの方の記事になります。

昨日は@yamadamnさんの「WebLogic Server 12.1.1 on Mac OS X」でした。


Contexts and Dependency Injection (CDI/JSR-299)


今日はJavaEEのCDIの話をしようと思いましたが、

JavaEEとかよくわかってないんで、

いろいろと調べているうちに、

全然別のDependency Injection(JSR-330)の方向に進んでいってしまいました。

まあ、すこしは関係あるので、いいかな…


一応、JSR-299とJSR-330の簡単な関係を…


元々のJSR-299に対してgoogleのボブ・リーがこれじゃちょっとみたいな感じで

ロッド・ジョンソンとともに提案したのがJSR-330ですね。

で、JSR-299はJSR-330の上のレイヤーで動作するという感じになっています。

こちらを参考


tckを通してみよう


さて、CDIとDIを使ってなんか遊んでみようと思って、

Maven Repositoryを漁っていたら、

javax.injectのtckを発見しました。

ということで、急遽JSR-330を実装してみることにしました。

(終わってないけどね…)


プロジェクトの作成


恥ずかしながら僕はpom.xmlが書けないので、

build.gradleはこんな感じです。





tckの実行


最初、tckのソースも読まずにテストをやろうとして、

いろいろと粘っていましたが、

テストはこうやって書けよとtckのjavadoc

書いてありました。





Carというクラスはtckの中で書かれているインターフェースです。

で、tckにCarのインスタンスを渡せば、

テストしてくれるようです。


TDD?


テストを先に書いたので、org.mikeneck.inject.Injectorなんてのは存在しません。

だから、コンパイルが通るように適当に実装を書いています。





まあ、当然ですが、ヌルポです。




実装についての説明もtckに書いてあります


tckのjavadocを読むと、

Carクラスの実装はConvertibleクラスだそうです。

また、他にもインターフェースと実装の指定が書いてあり、

それらを参考にしながら実装することになるようです。


昨日からやり始めたことなんで、

まあ、ちょっとやそっとではできなそうですね。

適当に時間の合間を縫って実装してみたいと思います。

(かなり難しい気がする…)


なお、Google Code上にあるWikiによると、

tckを通った実装が、5つあるようです。



おしまい


なんかJavaEEとほとんど関係のない話になってしまいました。

すんません。


きっと、明日(2012/12/11)記事を書いてくださる@matsumanaさんが

話をいい方向に戻してくださると思います。

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

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


しかし、この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年6月7日木曜日

Spring Roo始めました。 その3

今日は、JSR303 (Bean Validation)の話


制約を設ける


セレブが入会する会員制クラブをつくろう!としています。

そこで、メンバーテーブルを作ります。



基本的な制約をここでかけています。

  • 名前
    • not null / 最低2文字 / 最大40文字
  • 姓名
    • not null / 最低3文字 / 最大40文字
  • 年齢
    • 最低20 / not null
  • 収入
    • not null
  • 会員登録日
    • 過去

出来上がったMembershipクラスは次のようなコードになっています。



この段階で自動生成されたテストを流します。



自動生成されたテストのデータというのは、Spring Rooが出力したテストデータ生成用のクラスMembershipDataOnDemandクラスによって作成されます。


ビジネス的な制約の導入


ところでセレブがくる会員制クラブですので、若いヤンキーニーチャンを入会させたくありません。

そこで、30歳未満の人には高めの収入(100,000ドル以上)を持っていることを制約条件に加えようと思います。

まず、テストに上の条件のユーザーの制約を書いてみます。



テストを流します。



30歳未満で収入の低い人が入会できないことを確認するテストyoungMemberCannotBeAppliedが落ちていることがわかります。

では、この条件を実装していきます。

ここで使うのがJSR303のBean Validation APIの@AssertTrueです。

@AssertTrueは指定したフィールドまたはメソッドがtrueを返すことを強制する制約です。

これを用いて条件を実装します。



では確認のためにテストを流しましょう。



追加したテストの方は通ったようですが、あれれ、自動生成されたテストは軒並み落ちていますね。

まあ、勝手に追加した条件なのでSpring Rooの方では検知できないのでしょう。よく考えればそうですね。


git


んで、よく見てみると、モデルに変更を加えた後になぜかAspectJのコードが変化しているようです。



何が変わったのでしょうか?





40文字以上だったら40文字に直してくれてたコードが、直してくれなくなっていますね。

また、年齢が20未満だったら20に直してくれていたコードが直してくれなくなっていますね。

日付についても現在より10,000,000Lだけ前に修正してくれていたコードが現在時間+αになるように変更されています。

Spring Roo君はなんてことをしてくれるんだ!


Push in


こうなったら、AspectJのコードをJavaの方にPush Inして調整する必要があるようです。

変更されてしまったメソッドにカーソルを当てた状態で、IntelliJ IDEAのRefactorメニューからPush ITDs Inを選択します。

(eclipse…知らん…)

その後、MembershipDataOnDemand.javaのPush Inされたメソッドをテストが通る(と言うよりはビジネス的に問題のないデータが提供される)ように修正します。



では再度テストを実行してみます。



はい、通りました。

結論


はい、Spring RooのBean Validation API関連の作業について見てきました。

多少面倒なところはあるもののテストデータを自動で生成してくれたり、テストを自動で生成してくれているところは助かります。

ただ、複数のフィールドにまたがるビジネス上の制約についてはRooはアホなくらい鈍感ですね。

このあたりは慣れるしかなさそうです。

2012年3月31日土曜日

@HIROCASTER さんに刺激されて書いてみた、新社会人Javaプログラマー向け、失敗しないんじゃないかなと思う書籍

こんにちわ。

春ですね。

幸せな気分になる季節です。

なぜって、素敵なスーツ姿の新入社員と思しきお姉様桜が見頃の季節だからです。

みけです。

新社会人向けの絶対に失敗しない書籍


僕が触発されたHIROCASTERさんのエントリー

新社会人Webプログラマ向け、絶対に失敗しない参考書・推薦書

をどうぞ。

Javaプログラマー向け


P系言語は色々と書かれていたんですけど、Javaがないやってことで、

こっからがこのエントリーの本番。

JavaはとにかくIDEが重要

というわけで、JavaはとにかくIDEをどれだけ手になじませるかが重要です。

そこで次の三冊。







の本はEclipseの定番中の定番。

閉鎖環境では大抵Eclipseが使われていることが多いので、

これを読んでおきましょう。

なお、私のような不良社会人がIntelliJ IDEAという

イケナイ有償のIDEを進めてくることがあります。

本当にイクナイですね~。


真ん中の本はNetBeansの本。

NetBeansはOracleがリリースしているJavaのIDEです。

JavaEE開発する場合は、こちらの方が充実していることが多いようです。(棒

(NetBeans未使用なオレがなんてことを言う)


の本はGoogle Appengine + Slim3の本ですが、

実はEclipseを効率良く使う方法が色々と掲載されています。



いや、そもそもJavaがアレで…

まあ、このあたりがいいでしょう。








の本は幅広く抑えた一冊という感じ。僕も時おり読みます。


真ん中の本はJavaの仕様の本。この本を読まないと何も始まりません。

なお、このブログの執筆者はこの本を読んだことは…おっと消しゴムが落ちてしまった…


の本はJavaのコードをより良くするための本です。コード書く時に迷ったら読んでいます。

会社用と自宅用の二冊を準備しておくことが望ましいです。


品質

ドクター・ペッパーを買ったはずなのに、コカ・コーラが出てきたら嫌ですよね。

だから、品質はすごい大切です。

そのためにはプログラムは動かして試さないといけません。

そのためにはソフトウェアは動くことが求められます。








の本はHIROCASTERさんの紹介にもあった本です。

一回くらいは写経して、最後あたりにがっかりしてみるのもいいかもしれません。


の本はJUnitに絞った本です。

結構、現場でのシーンに即して書かれた本なのでお勧めです。

特にDbUnitの使い方やJUnit4で書かれているあたりは実用性が高いです。


よりイケてるJavaプログラマーになるために

やっぱりGroovyですよ。ということで…




Groovyで日本の本といえば、この一択です。

Groovy in Action忘れてた…orz



チケット管理


きっと入社した会社ではチケット管理していますよね。

チケット管理について学んでおきましょう。

会社で使っていなかったとしたら、

もう少しで会社が滅びる可能性があるので

チケット管理を導入するために学んでおきましょう。








の本は最もイケてるチケット管理システムJIRAの本です。

JIRAは徹底的に気の利いたチケット管理システムです。

一度触ると他のチケット管理なんて…ってなってしまいます。

一部ノイズが入ってしまいました。

なんたって、Javaでできているんですよー。


真ん中の本は比較的良くできているオープンソースのチケット管理システム

Redmineの有効な活用の仕方が書かれた本です。

ちょうど私が仕事の進め方ってって考えている時に出版された本で、

非常に参考になりました。


の本はまた別の比較的よくできているオープンソースのチケット管理システム

Tracの本です。

まあ、素のままTracを使う人はいないので、Trac Lighteningとか使うと思いますが、

Tracでのチケット管理全般について書いてあると思います。

思いますというのは、僕が読んだことがないからです。

僕が持っているのは

Trac入門 ――ソフトウェア開発・プロジェクト管理活用ガイドなので、

これを紹介したかったのですが、絶版になっているのかAmazonで入手不可っぽいです。



その他の書籍を会社や僕はオススメしているよ。ってのがあれば、

Twitterで教えてください。
(文章パクリ)

2012年3月25日日曜日

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

ども。

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

現状のコード


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





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

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

これがかなりきつい。

ExecutorServiceを使うといいのでは?

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

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


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月18日土曜日

Developers Summit 2012 17-D-7 実践Androidデベロッパーズテストにて講演してきました。

通称デブサミ2012の二日目最終セッションでAndroidテスト部の一員として講演してきました。

講演資料はスライドシェアにあります。
こちらの26ページから56ページを担当させて頂きました。

また、その期間のツイートはtogetterにまとめてあります。

なお、今回の講演にあたってサンプルで作成していたアプリケーション「お小遣い・ログ」のソースコードはGitHubにて公開しています。

Twitter上で私をフォローしていただいている方はご存じの方も多いかもしれませんが、最近の私のTwitterのプロファイルは「冬眠中」となっています。

実は、これは文字通り意味していまして、若干過眠症気味な状態になっています。

そういったわけで、今回資料を作成するにあたっては、メンバーの皆さん @snskさん@nowsprintingさん@ussy00さんには大変ご迷惑をおかけいたしました。

ここで感謝を表したいと思います。

また、今回の資料を作成するにあたって、何度か会社を休んでしまいました。

相談に乗っていただいた株式会社トップゲートの加藤社長ならびに同僚の皆様にも大変感謝しております。


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月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にはauthorpriceというメンバーが存在します、いや、するはずです。

では、本当に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.0constraintsValidationテストをする場合は、黙ってmockForConstraintsTestsを使っとけという話ですな。


2012年1月3日火曜日

はじめてGrailsをさわるオッサンがGrails2.0でDomainを作ってみた


An elder engineer, new for Grails, got involved into a trouble on unit testing of Grails2.0 domain classes.

Grails2.0



昨年末にGrails2.0がリリースされました。
というわけで、正月のお休み期間を利用して触ってみることにしました。

とはいえ、実は初めてGrailsを触るので、『Grails徹底入門』をお手本に写経することにしました。








Grailsというのは、SpringHibernateGroovyを元にして作られたJVM上で動作するWebアプリケーションフレームワークです。
設定よりも規約に準拠することにより高い生産性を発揮することを目標に作られています。
また、GroovyとJavaの親和性は高く、既存のJava資産を有効に活用できるフレームワークでもあります。


Domain


あらまし


アプリケーションを作成する場合、まず問題がどういった要素が関係しているか分析を行います。
これらの要素と関連はWebアプリケーションとか、Androidアプリケーションとかそういったプラットフォームに依存することなく存在します。
こういった要素と関連のことをModelと言ったり、Domainと言ったりします。
GrailsではDomainという言葉で指しているようです。
(テキトーなオッサンなのでツッコミ( `・∀・´)ノヨロシク)

BookとPublisher


『Grails徹底入門』では本屋さんを模してアプリケーションを作成していますので、本(Book)とか、出版社(Publisher)とかそういった類のDomainを定義しています。
というわけで、内容をパクって参考にして、BookとPublisherの関係を書いてみました。


関係性


まあ、難しい関係ではありませんね。
Bookからみて、Publisherは…

  • 一つだけある。
  • 必ず存在する。

という関係があります。

逆にPublisherからみて、Bookは…

  • 複数ある。
  • 存在しないこともある。

という関係があります。

まあ、こういう話は、ER図の本とか、データベース設計の本などの方が詳しいので、そちらに譲りましょう。
というわけで、このドメインをGrailsで作っていきたいと思います。

ドメインクラスの作成


create-app


まず、プロジェクトを作成します。


$ cd workspace
$ grails create-app BookShop


これで、workspaceディレクトリの下に次のような構造をもつBookShopプロジェクトディレクトリが作成されます。


なお、プロジェクト名が違っていますが、そこは、ほら、その、なんというか、大人な感じに、つまりいい感じに読んでいって下さい。


create-domain-class


次に、ドメインクラスを作成します。


$ cd BookShop
$ grails create-domain-class Publisher
$ grails create-domain-class Book


すると、次のようにDomainクラスとTestクラスが作成されます。

  • grails-app
    • domain
      • bookshop
        • Book.groovy
        • Publisher.groovy
  • test
    • unit
      • bookshop
        • BookTests.groovy
        • PublisherTests.groovy


これらの自動生成されたクラスを編集してドメインを実装していきます。

あと、Orderとかいうクラスがすでに追加されていますが、そこも、ほら、あの、なんていうか、その、大人な感じで( `・∀・´)ノヨロシク。

ドメインクラスの実装


メンバーを与える


先ほど書いた図を元にドメインクラスにメンバーを与えていきます。

Publisher.groovy

class Publisher {

    String name

}


Book.groovy

class Book {

    String name

    String author

    int price

    Date releaseDate

    String isbn13

    String imageUrl

    String description
}


はい、ここまでは難しくありません。

関係に関するメンバーを追加する


で、これに先ほどの関係のメンバーを付与します。

まず、Publisherの方ですが、Bookを複数持ちますので、hasManyを用います。

Publisher.groovy

class Publisher {

    String name

    static hasMany = [books: Book]
}


これによって、PublisherクラスにはbooksというList<Book>クラスのメンバーを持つことになります。

同様にBookクラスにもPublisherへの関連性をもたせます。
BookクラスはPublisherクラスを唯一つ持ちますので、belongsToを用います。

Book.groovy

class Book {

    String name

    String author

    int price

    Date releaseDate

    String isbn13

    String imageUrl

    String description

    belongsTo = [publisher: Publisher]
}


これによって、BooksクラスにはpublisherというPublisherクラスのメンバーを持つことになります。

ちなみに、これはGroovyのうれしいところですが、

Groovy ではメンバーを記述するだけで、自動的に setter / getter を自動生成してくれる

という機能があります。


Plain Old Java Object : POJO みたいに、 Plain Old Groovy Object : POGOなんて呼ばれているとかなんとか…

制約 (Constraints) を追加する


さて、さきほど関係性だけを記述しましたが、これではまだ十分ではありません。

  • 0個存在するとか、1個は必ず存在するとかの制約 (Constraints) の記述がない
  • 本の名前がないとか、著者がないとか、価格がマイナスとかっておかしい
  • 出版社の名前がないのというのもおかしい

関係性に関する制約にとどまらず、基本的な事柄に関する制約条件が満たされていません。

というわけで、これらの制約を盛り込んで行きましょう。

Publisher

まずは Publisher の方からですが、こんな制約が必要でしょうか…

  • 会社名の空白は禁止
  • 会社名が他の会社とかぶってはいけない

まあ、実際には2の制約はないでしょうけど、まあここではそういうことにしておきましょうwww

このような制約を与える場合に使うのが、 constraints です。

では、 Publisher に制約を与えてみましょう。

Publisher.groovy

class Publisher {

    String name

    static hasMany = [books: Book]

    static constraints = {
        name(blank: false, unique: true)
    }
}


追加した部分はこんな感じです。

  • 会社名 (name) に対しては、空白 (blank) は、禁止 (false) 。
  • 会社名 (name) は、唯一 (unique) に決まること (true) 。

これだけでいいんですか?これだけでいいんです( ー`дー´)キリッ

制約に関するテストを書く


疑心暗鬼に陥っているときは、テストを書いて安心するのがよいらしいです。

PublisherTests.groovy

@TestFor(Publisher)
class PublisherTests {

    @Test
    void validateName() {
        def publisher = new Publisher(name: 'hoge')
        assert publisher.validate()
    }
}


とりあえず、名前のある会社は大丈夫だよねっていうテストを書いてみました。
ちなみに、コンストラクターのところにある記号、name: 'hoge'というのは、Groovy特有の書き方で、Javaで書くとこんな感じになります。


@TestFor(Publisher)
class PublisherTests {

    @Test
    void validateName() {
        def publisher = new Publisher()
        publisher.setName('hoge')
        assert publisher.validate()
    }
}


では、実行しましょう。

Grailsのテストサポート


Grails interactive


さて、テストを実行したいところですが、コマンドgrails test-app bookshop.Publisherと実行するだけです。

実行するだけなのですが、色々とコマンドを覚えるのが面倒ですね。

そこで、grailsのコンソールを起動して、そちらに任せてしまいましょう。

ちなみに、grailsのコンソールとテキトーに書いていますが、正確にはgrails interactiveというらしいです

grailsのコンソールは何がいいかというと、もしコマンドがわからなければ、Tabを押すだけで、何を入力するべきか表示してくれます。

起動


起動方法は簡単です。


$ grails


と入力するだけです。

こんな感じでgrailsのコンソールが立ち上がります。


$ grails
| Enter a script name to run. Use TAB for completion:
grails>


何をするんですか?


何をすればいいかわかりませんね。Tabを押して下さい。


いろいろとコマンドが表示されます。

自分のやりたいコマンドを選ぶだけで構いません。

そうだテストをしよう


今はテストをやりたいので、test-appコマンドを入力します。

でも、このコマンドはすべてのテストを実行するコマンドなので、若干不便です。

たった一つのクラスを変更しただけなのに、すべてのテストを実行するのは後々時間がかかって大変です。

そういうのはJenkins先生とかにお願いしましょう。

そこで、test-appまで入力して、Tabを押して下さい。


どういうテストができるのか一覧が表示されます。

やりたいのはPublisherのテストなので、それっぽいやつを途中まで入力して、


Tabを押します。


お、なんかいい感じに入力できるコマンドが指定されてきました。

Publisherも途中まで入力して、


Tabを押します。


勝手に補完してくれますね。これで、Publisherだけのテストを実行できます。

では、おもむろに実行!


成功ですね。

え〜、ここでも、テストの個数が若干異なっていますが、そこは大人の事情ということで…

ちゃんとPublisherのテストを書く


blankに対するテストを書く


さて、今のテストはただ単に成功するだけの条件を書いたので、あまり意味のあるテストではありません。

そこでnameのvalidationに対するテストを書いていきます。

  • namenullだったらエラーとなるか?
  • nameが長さ0の文字列だったらエラーとなるか?

まず、この二つは確実に抑えておきたいところです。

これについてテストを書いていきます。

PublisherTests.groovy

@TestFor(Publisher)
class PublisherTests {

    @Test
    void validateName() {
        // name が null の場合
        def publisher = new Publisher()
        assert publisher.validate() == false

        // name が '' の場合
        publisher = new Publisher(name: '')
        assert publisher.validate() == false

        // name が長さ1以上の文字列の場合
        publisher = new Publisher(name: 'hoge')
        assert publisher.validate()
    }
}


テストを実行。


はい、成功です。

uniqueに対するテストを書く


さて、Publisherの制約条件にuniqueというのがありました。

読んで字の如くで、同じ名前の会社が登録されていたら、エラーとするというものなのですが、データベースが必要になってきます。

面倒くさそうになって来ました。

そこでGrailsでは、Unitテストにおいてデータベースがなくてもキャッシュだけでテストを行えるような仕組みを提供しています。

mockForConstraintsTests(Class<T>, List<T>)を用いてテストを実行します。

PublisherTests.groovy

@TestFor(Publisher)
class PublisherTests {

    @Test
    void validateName() {
        def existingPublisher = new Publisher(name: 'exists')
        mockForConstraintsTests(Publisher, [existingPublisher])

        // name が null の場合
        def publisher = new Publisher()
        assert publisher.validate() == false

        // name が '' の場合
        publisher = new Publisher(name: '')
        assert publisher.validate() == false

        // name がすでに存在する会社の名前と一致する場合
        publisher = new Publisher(name: 'exists')
        assert publisher.validate() == false

        // name が長さ1以上の文字列の場合
        publisher = new Publisher(name: 'hoge')
        assert publisher.validate()
    }
}


では、テストを実行してみましょう。


テスト通りました。

締め


以上、gdgdな感じの紹介になりましたが、こんな感じでDomainを作っていきます。

本当はもっとハマリどころがあるんですが、その前段まででめちゃくちゃ長くなってしまったので、ハマリどころについては次回やります。

ん、インストール方法が載っていない?

zipをダウンロードしてパスを通してあげて下さい。


2011年12月24日土曜日

hamcrestを拡張してmoreThanとか作ってみた

JavaAdventCalendarの24日目のエントリーです。

最初はenumに関してエントリーを書こうとしていたのですが、そのネタについてコードを書いている途中で、どうしても気持ち悪いテストコードを書く羽目になったので、なんかいい表現ないかなと考えているうちに、hamcrestの拡張を書いてしまいました。

というわけで、タイトル

hamcrestを拡張してmoreThanとか作ってみた


です。

そもそものそもそも


もともと書いていた気持ち悪いテストコード…

@Test
    public void testCompare() {
        Trump queen = Trump.valueOf("Queen");
        Trump king = Trump.valueOf("King");
        assertThat(queen.compareTo(king) < 0, is(true));
    }

    private enum Trump implements Comparable<Trump> {
        King {
            int value = 13;
            @Override
            public int value() {
                return value;
            }
            @Override
            public int compareTo(Trump trump) {
                return this.value() - trump.value();
            }
        }, Queen {
            int value = 12;
            @Override
            public int value() {
                return value;
            }
            @Override
            public int compareTo(Trump trump) {
                return this.value() - trump.value();
            }
        }
        abstract public int value();
        abstract public int compareTo(Trump trump);
    }


assertThatの中が気持ち悪い…

まあ、別にboolean result = queen.compareTo(king) < 0を変数として取り出せばよいだけの話といえば、それまでなのですが、やっぱりなんか気持ち悪い。

で、junit more thanググってみたけど、こんな感じでした(´・ω・`)

というわけで、moreThanみたいなことをやりたかったので、自前で作って見ることにしました。

拡張してみよう!


で、コードはこんな感じ。

org.hamcrest.core.MoreThan.java

package org.hamcrest.core;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;

public class MoreThan<T extends Comparable<T>> extends BaseMatcher<T> {

    private final T matcher;
    private final Class<T> klass;

    public MoreThan(T matcher) {
        this.matcher = matcher;
        this.klass = (Class<T>) matcher.getClass();
    }

    @Override
    public boolean matches(Object o) {
        if(o.getClass() == klass) {
            T object = klass.cast(o);
            int result = matcher.compareTo(object);
            return result < 0;
        } else {
            return false;
        }
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("more than ").appendValue(matcher);
    }

    @Factory
    public static <T extends Comparable<T>> Matcher<T> moreThan(T value) {
        return new MoreThan<T>(value);
    }
}


拡張して自分好みのMatcherを作るにはorg.hamcrest.BaseMatcher<T>を継承するようです。

メソッドmatchesは実際の値の比較を記述します。
メソッドdescribeToはテストが失敗したときに表示される文章を記述します。

使い方


使い方は至って簡単です。

MoreThanTest.java

@Test
    public void testCalendarCase() {
        Calendar cal1 = Calendar.getInstance(
            TimeZone.getTimeZone("Asia/Tokyo"));
        cal1.set(2011, 12, 24, 15, 54, 55);
        Calendar cal2 = Calendar.getInstance(
            TimeZone.getTimeZone("Asia/Tokyo"));
        cal2.set(2011, 12, 24, 15, 54, 56);
        assertThat(cal2, moreThan(cal1));
    }


ちなみに、残念な所があります。
  • intlongなど、異なる型の値を比較できない。

まあ、そのあたりは型安全様にあわせてあげて下さい。

その他、LessThanLessThanEqualMoreThanEqualなども作ってみましたので、まあ、興味があったら見てツッコミを下さい。

明日のJava Adventカレンダーは、daisuke-mさんです。


余談


このエントリーを掲載した後、こんなツイートが寄せられました。




うぉ、本当だ…!


さらには、



なんと!。
JUnit4にバンドルされているhamcrestは古いバージョンだと!

そして、極めつけは、


というわけで、明日のdaisuke_mさんのページを見ると…

都元ダイスケ IT-PRESS : [Java][test]hamcrestのMatcherメモ

バッチリまとめられておる。


というわけで、車輪の再発明をしてしまったようだ…。


そこで、ソースを読んでみる


まあ、情弱なのは仕方ないので、もうちょい突っ込んでみてみる。


hamcrest-libraryのソースはcode.google.comにあるようです。

org.hamcrest.Matchers.javaというソースはなかったのですが、その本体となるorg.hamcrest.number.OrderingComparisonというクラスがあったので、それを読んでみました。


/*  Copyright (c) 2000-2009 hamcrest.org
 */
package org.hamcrest.number;

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

public class OrderingComparison> extends TypeSafeMatcher {
    private static final int LESS_THAN = -1;
    private static final int GREATER_THAN = 1;
    private static final int EQUAL = 0;
    private final T expected;
    private final int minCompare, maxCompare;

    private static final String[] comparisonDescriptions = {
            "less than",
            "equal to",
            "greater than"
    };

    private OrderingComparison(T expected, int minCompare, int maxCompare) {
        this.expected = expected;
        this.minCompare = minCompare;
        this.maxCompare = maxCompare;
    }

    @Override
    public boolean matchesSafely(T actual) {
        int compare = Integer.signum(actual.compareTo(expected));
        return minCompare <= compare && compare <= maxCompare;
    }

    @Override
    public void describeMismatchSafely(T actual, Description mismatchDescription) {
        mismatchDescription.appendValue(actual).appendText(" was ")
                .appendText(asText(actual.compareTo(expected)))
                .appendText(" ").appendValue(expected);
    }

    public void describeTo(Description description) {
        description.appendText("a value ").appendText(asText(minCompare));
        if (minCompare != maxCompare) {
            description.appendText(" or ").appendText(asText(maxCompare));
        }
        description.appendText(" ").appendValue(expected);
    }

    private String asText(int comparison) {
        return comparisonDescriptions[comparison + 1];
    }

    /**
     * @return Is value = expected?
     */
    @Factory
    public static > Matcher comparesEqualTo(T value) {
        return new OrderingComparison(value, EQUAL, EQUAL);
    }

    /**
     * Is value > expected?
     */
    @Factory
    public static > Matcher greaterThan(T value) {
        return new OrderingComparison(value, GREATER_THAN, GREATER_THAN);
    }

    /**
     * Is value >= expected?
     */
    @Factory
    public static > Matcher greaterThanOrEqualTo(T value) {
        return new OrderingComparison(value, EQUAL, GREATER_THAN);
    }

    /**
     * Is value < expected?
     */
    @Factory
    public static > Matcher lessThan(T value) {
        return new OrderingComparison(value, LESS_THAN, LESS_THAN);
    }

    /**
     * Is value <= expected?
     */
    @Factory
    public static > Matcher lessThanOrEqualTo(T value) {
        return new OrderingComparison(value, LESS_THAN, EQUAL);
    }
}



@Factoryのあたりを眺めてから、describeToなどを眺めると、思わずニヤリとしてしまいますね。
やっぱり本家本元のソースには勝てんかったか…。


で、なんで、Matchersがないのか気になったのですが、ひょっとしてhamcrest-generatorあたりで自動で処理しているのかな~などと思ってみたりしましたが、まだそこまでちゃんとソースを読んでおりません…

ん~、まあ、でも少し勉強になりました。

ご指摘をくださった皆様、大変有難う御座います。