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

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年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というのは、Spring、Hibernate、Groovyを元にして作られた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に対するテストを書いていきます。

  • nameがnullだったらエラーとなるか?
  • 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年7月4日月曜日

Selenium勉強会に参加してきました。

昨日(2011/07/03)、Selenium勉強会に参加してきました。

最近は殆どの業務システムはWeb化しているので、
テストの自動化には避けられないのがSelenium。

しかしながら、Seleiniumについては触ったことがなかったので、
勉強がてら参加してきました。

というか、この勉強会の趣旨は、
講師なし、皆で学ぶというものだったので、
かなりぴったりな勉強会でした。

今日はそこで勉強したことのまとめを
書こうと思います。

このページをご覧になるであろう方は、
ほとんどの方はSeleniumを触ったことの
ある方だと思いますので、簡単なことしか
書かれていません。

  • Selenium IDEのインストール
  • Selenium IDEでテストケース作成
  • Selenium RCのインストール
  • Selenium RCでテストケース作成

Selenium IDEのインストール
  • Fire Foxをインストールする。
  • Selenium IDEをインストールする。

    • Windows の場合、Fire FoxでSeleniumのサイトからダウンロードリンクをポチる。
    • Mac/Linuxの場合、Fire Foxのツール→アドオンからSeleniumを検索して、ダウンロードする。

Selenium IDEでテストケース作成
  • Fire Foxを起動する。
  • Selenium IDEを起動する。
  • Selenium IDEで新規テストケースを作成するを実行
  • 赤丸ボタン(記録ボタン)を押す。
  • 操作をする。
  • 出力されて欲しい文字列を画面上で選択して右クリック→verifyほげほげを選択する。
  • 赤丸ボタンを押して、記録を終了する。

Selenium RCのインストール
前提条件 : Java Development Kitがあること。
  • Selenium RC のスタンドアローンサーバーをインストールする。
  • 以上w

Selenium RCでテストケース作成(Java)
前提条件 : JavaのIDEがあること。
  • Selenium RC のJavaバインディングをダウンロードする。
  • IDEでJavaプロジェクトを作成する。
  • Javaプロジェクト上で、Javaバインディングをライブラリーに追加する。
  • Selenium IDEで作成したテストケースをJavaファイルに出力する。
  • 出力したJavaファイルをIDEに取り込む。

Selenium RCでテスト実行
  • コマンドプロンプトで次のコマンドを実行する。
  • java -jar selenium-server-standalone-.jar
  • テストケースを実行する。

といった基本を学んできました。

結構、オレオレメモなのであまり参考にならんかも…
まあでも、一人でSeleniumを勉強するよりは道連れ初心者同士が集まって勉強すると、不安がなくなりますね。

2011年4月17日日曜日

JUnitでWebサービスのモックを作る

ソーシャル系のアプリやほかのWebサービスと連動するアプリの単体テストをする場合、
対向するWebサービスをどうしても利用しないとテストできないようなケースが発生する。

たとえば、Twitterアプリを作成している場合など。

Twitterのようなすでに動いているサービスが対向するwebサービスであれば、
テスト用のアカウントを作成しておいて、
そこにテスト用のTweetを入れておけばよいのではあるが、
しかしエンタープライズな場合だと、
そのような準備をできないこともある。

SIerの場合、後の工程(つまり総合試験)にそれらのテストを持って行って、
人月投入するというパターンがあるが、それはそれで、非常にまずい。

そんなわけで、手軽にWebサーバーを構築したいのだが、
tomcatにサーバーをまた立てて、モックのアプリサービスを書いて、
云々というのも非常に面倒臭い。

そこで登場するのがJettyという軽量Webサーバーです。
これはJUnitでもサーバーを立ち上げられる優れもので、テスト用にも利用出来るなかなか優れたういやつです。
ちなみに、現在はバージョン8まで登場しているそうですが、本稿で使用するのは7.3.1です。



さて、早速コードをと言いたいところですが、
今回のサンプルは先に仕様の説明から。
  • Webサーバーはlocalhostにポート3000で立てます。
  • どのようなリクエストが来ても、レスポンスコード200 : OKを返します。
  • コンテンツタイプは"application/json"を返します。
  • メッセージ部分は{"test_result" : "ok"}というjsonが設定されます。

では、早速JUnitのテストコード@BeforeClassと@AfterClassからどうぞ。


public class HttpGetResourceTest {

    private static final String TEST_RESULT = "{\"test_result\" : \"ok\"}";

    private static final int PORT_NUMBER = 3000;

    private static final String LOCAL_HOST = "localhost";

    private static Server server;

    /**
     * サーバーを起動する。
     */
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        InetSocketAddress inet = new InetSocketAddress(LOCAL_HOST, PORT_NUMBER);
        server = new Server(inet);
        
        // 後述するHandlerの実装。
        Handler handler = new RequestHandlerImpl();
        server.setHandler(handler);
        
        server.start();
    }

    /**
     * サーバーを終了する。
     */
    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        server.stop();
        server.destroy();
    }



@BeforeClassではサーバーを起動して、@AfterClassはサーバーをシャットダウンします。

一応このテストクラスが実行されている間は、サーバーはローカルホスト3000番で起動します。

実際にどのような処理がなされるかは、このテストの内部クラスRequestHandlerImplに記述します。

では、RequestHandlerImplをどうぞ。


    private static class RequestHandlerImpl extends AbstractHandler {

        @Override
        public void handle(String target, Request baseRequest,
                HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
            
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            response.setStatus(HttpServletResponse.SC_OK);
            
            Writer writer = response.getWriter();
            writer.write(TEST_RESULT);
            
            HttpConnection connection =
                HttpConnection.getCurrentConnection();
            Request req = connection.getRequest();
            req.setHandled(true);
        }
    }


ここでは本当に先程の仕様通りに
  • どのようなリクエストが来ても、レスポンスコード200 : OKを返します。
  • コンテンツタイプは"application/json"を返します。
  • メッセージ部分は{"test_result" : "ok"}というjsonが設定されます。

となっています。
あまり説明いらないですね。

では、サンプルのテストコードを書いてみましょう。
なお、このサンプルではApache HttpComponentsを利用しています。


    @Test
    public void testIsServerReady() throws Exception {
        URI uri = URIUtils.createURI("http",
                LOCAL_HOST, PORT_NUMBER,
                "/search",
                "user=mike_neck&start=10",
                null);
        HttpGet hGet = new HttpGet(uri);
        HttpClient client = new DefaultHttpClient();
        HttpResponse res = client.execute(hGet);
        
        HttpEntity entity = res.getEntity();
        
        if(entity != null){
            StringBuilder builder = null;
            InputStream input = null;
            try {
                input = entity.getContent();
                InputStreamReader reader =
                        new InputStreamReader(input, "UTF-8");
                builder = new StringBuilder();
                while(reader.ready())
                    builder.append((char)reader.read());
            }finally{
                input.close();
            }
            assertThat("testIsServerReady",
                builder.toString(),
                is(TEST_RESULT));
        }else{
            fail();
        }
    }


適当なクエリーをつけてGetで呼び出しました。
ここの部分は実際のクラスを入れておく部分ですね。



と、まあこんな感じなわけですが、
多少依存性がありますので、その情報も書いておきます。

必要なライブラリーは
commons-logging.jar
jetty-continuation.jar
jetty-http.jar
jetty-io.jar
jetty-server.jar
jetty-util.jar
servlet-api.jar

です。

皆さん、Web連携アプリのテストもしっかりとやりましょう。