WEBページでの操作(テスト)のカバレッジを計るには
selenium+jUnit(djUnit)の組み合わせで可能ですか?
seleniumはRC(RemoteControl)を使います。
seleniumコマンドは、jUnit内から呼び出すことができ、webページ上での操作を実行できます(正確には、クライアントドライバ(RCに含まれるライブラリ)でseleniumコマンドを書き、seleniumサーバ上でwebブラウザ操作、操作結果のテストを実行します)。
参考ページ:テストの作成と実施(SeleniumRC−Java)
このテストケースをdjUnitで実行してやれば、WEBページでの操作の結果、ページクラスの処理をどの程度網羅したかを計るカバレッジを計測できると思います。
また、クライアントドライバでDBを初期化したり、
参考ページ:SeleniumRC ClientDriverでリモートサーバのDB初期化
拡張for文を使う理由
JDK1.5から利用できる拡張for文を使う理由をまとめました。
- 見た目がシンプルだから
確かにシンプルです。
List<String> strList = new ArrayList<String>(); //要素追加 //これまでのfor文 for(int i = 0; i < strList.length; i++){ System.out.println(strList.get(i)); } //これからのfor文 for(String str : strList){ System.out.println(str); }
しかし、理由はこれだけではありません。
- パフォーマンスも上がるから
実はこれは半分嘘です。
strArrayがArrayList型であった場合、「これまでのfor文」のようにgetした方が30%程度パフォーマンスが上がります。
ですが、仮にstrArrayがLinkedList型であった場合、「これまでのfor文」のようにgetをすると、99%以上パフォーマンスが下がります。
参考ページ:JavaはIteratorを使うべき理由
つまり、strArrayがLinkedList型である場合に、拡張for文を使うとパフォーマンスが上がります。
では、forで回すリストの実装によってfor文を使い分ければいいのではないでしょうか?
しかし、拡張for文にはもう一つ良い点があります。
- コードの柔軟性が上がる
拡張for文を使っていれば、for文に渡されるオブジェクトがArrayListかLinkedListか分からない場合でも、「99%以上パフォーマンスが下がる」という心配をしなくて済みます。
また、拡張for文は、Listインタフェースの実装型をイテレータを使ったコードに変換し、配列の場合にはインデックスアクセスするコードに変換してくれます。
参考ページ:J2SEトラの穴-拡張 for 文ー実際はどうなってんだ ????
このコンパイラの翻訳によって、for文に渡すリスト又は配列に適したコードが生成されることが分かります。つまり、拡張forさえ使えば何も考えなくていい、何も心配しなくていいということです。まずいことがあればAPIの裏でなんとかしてくれる、ということです。
CollectionUtilsについて
最近CollectionUtilsの存在を知ってからは、for文を書く回数も減って嬉しい限りなのですが、
ジェネリクスに対応しておらず、警告が鳴りっぱなしなのが残念でした。
が、ジェネリクス版CollectionUtilsがあったことを知りました。
早速、週明けにアーキテクトにねだってみます。
Listに入れたBeanを更新できない
本日の実装作業中にタイトルに書いた理由で詰まってしまいました。
正確には、Beanが保持していたプロパティ値にnullが代入されるというものです。
以下に順を追って説明します。
- 何が起こったか?
まず、Dxoによる更新処理について説明します。
Dxoにおけるコンバートメソッドは、大きく分けて以下の2通りに宣言できます。
//パターン1.srcを元にFooBeanオブジェクトのインスタンスを作成する FooBean convertToFooBean(FooBean src); //パターン2.srcを元にdestを更新する void convertToFooBean(FooBean src, FooBean dest);
今回、一部のプロパティに値を保持するインスタンスに対して、追記(その他のプロパティの値を更新)することを目的とし、以下のようなコードを書き、Dxoを呼び出していました。
//Listの準備 List<FooBean> fooList1 = new ArrayList<FooBean>(); List<FooBean> fooList2 = new ArrayList<FooBean>(); //Beanの準備 FooBean foo1 = new FooBean(); FooBean foo2 = new FooBean(); foo1.setAaa(1); foo2.setBbb("hoge"); //ListにDtoを格納する fooList1.add(foo1); fooList2.add(foo2); //Listに入ったDtoを更新する testDxo.convertToFooBean(fooList1, fooList2);
上のコードの実行した結果、fooList2のBeanが保持していたプロパティBbbはnullとなり、代わりにAaaに1がセットされました。
要はfooList2のfoo1のプロパティ値が、foo2と同じ内容に変換されてしまいました。
- そもそも追記できるのか?
追記する、という動作をそもそも実現できるのかを、以下のコードで確認しました。
//Dxoの呼び出し元 //Dtoの準備 FooBean foo1 = new FooBean(); FooBean foo2 = new FooBean(); foo1.setAaa(1); foo2.setBbb("hoge"); //Dtoを更新する testDxo.convertToFooBean(foo1, foo2);
//Dxoの宣言 @ExcludeNull void convertToFooBean(FooBean src, FooBean dest);
上記のコードにより、foo2のBbbの値はそのままに、Aaaの値を追記することができました。
ですが実は、プロパティ値を追記するには、「@ExcludeNull」でコンバートメソッドを修飾する必要があります。
このアノテーションにより、null参照の変換元プロパティは、変換対象から外れます。つまり、変換先にnullが代入されなくなります。
従って、foo1のプロパティBbb(null参照)は、foo2にコピーされなくなります。
以上で、プロパティの追記 という動作は実現できることは確認できました。
- 結局どうしたのか?
結局、「@ExcludeNull」を利用しても、Listに入れたBeanに追記することはできませんでした。
を確認すると、「@ExcludeNull」の使い方に関して、以下のような説明があります
「変換元JavaBeansのプロパティまたはMapのマッピングの値がnullの場合は,変換先のJavaBeansまたはMapに値を設定しないことをアノテーションで指定することができます.」
上の赤文字の「変換元JavaBeans」には「Listに入ったJavaBeans」はあてはまらない、ということなのだと理解し、Dxo変換による更新はあきらめました。
この問題に関して、情報をお持ちの方からのコメントをお待ちしております。
バリデーションに関して
昨日から、ダイナミックバリデータで使える共通コンポーネントを作っています。
- 要件
POST送信する複数の入力値に関してバリデーション(相関チェック)を掛けます。
- 参考ページ
- 調査履歴
- 上記参考ページの注意事項には「ページクラスのプロパティにはリクエストパラメータの値が反映されていません.」と明記してあります。
- そこで、「JSFライフサイクルとの関係」http://teeda.seasar.org/ja/extension/concept/lifecycle.html#JSF%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB%E3%81%A8%E3%81%AE%E9%96%A2%E4%BF%82 を参考にし、プロパティが反映されるタイミングを確認します。
- ここでは、「APPLY_REQUEST_VALUESフェーズ→PROCESS_VALIDATIONフェーズ→UPDATE_MODEL_VALUESフェーズ」の順に処理が進むとあります。
- それぞれのフェーズでの処理を「JSFのライフサイクル」http://www.thinkit.co.jp/free/article/0607/8/3/ を参考に確認しました。
- 確かではないのですが、恐らくページクラスのプロパティは、UPDATE_MODEL_VALUESフェーズで更新されます。
- また、[Seasar-user:12650]http://ml.seasar.org/archives/seasar-user/2008-January/012650.html を見ると、バリデーション実行時にはAPPLY_REQUEST_VALUESフェーズが完了しているため、UIComponent(?)にはリクエストパラメータが反映されており、UIViewRoot クラスから、パラメータが反映されたコンポーネントを直接取得できるようです。
<<編集中>>
条件制御について
ここでは、これまで編集中のまま放置していた「項目24 Condition」についての概要をまとめました。
項目24 Condition
概要
- teedaでは、条件によって表示コンポーネントの表示、非表示を切り替えるために、「条件制御」と呼ばれる機能を利用します。
- 本項目では、forEachやGridを利用して繰り返しデータを出力し、それぞれのデータに対して条件制御をかけるといったサンプルが用意されています。
- ここでは、サンプルの中でも使い道の分からなかった「Refresh」に関する説明をします。
サンプル「Refresh」
- 本サンプルは、teedaの拡張属性「te:refresh」の動作について確認するものです。
te:refreshが必要となる場面
- teedaでは、バリデーションエラーが発生した場合、isメソッドが呼ばれません。*1
- これにより、意図した条件制御が機能せず、Pageクラス上では表示されないはずのものが画面に残ってしまう場合があります。
- そこで、バリデーションエラーの際にもisメソッド呼び出すために、spanやdiv要素にte:refresh属性を設定します。
サンプル「Refresh」を使った動作確認方法
- まず、Pageクラス上では表示されないはずのものが画面に残ってしまう場合 を再現します。
- 次に、te:refresh属性 の動作を確認します。
- send.html内の 「te:refresh="true"」 を元に戻し、上記と同じ手順で画面操作します。
- バリデーションエラー発生後、「返信メッセージ「」を受信しました.」という文字列が表示されないことを確認します。
te:refresh属性に関する疑問気になる点
- te:refresh属性を、「バリデーションエラー時でも、メソッドを呼ぶために設定する」と考えると、getメソッドでも同じように呼べるんじゃないかと思うのですが、どうでしょう? 後で試して見ます。
- 追記:バリデーションエラー時でも、もともとgetメソッドは呼ばれるんですね。 ということで、一応解決しました。
以上です。間違い等ご指摘頂ければ幸いです。
*1:ちなみにPageクラスのプロパティは更新されず、またdoメソッドも呼ばれません。