山根@ぷーたろーです。
まだ、積極的にEJBを利用していないので、
お役に立てないかも知れませんが、一応cactusふぇちです。f(^_^;
On Tue, 7 May 2002 09:06:22 +0900
**************@***.***.**.** wrote:
> 【現在の対応】
> ・テスト対象EJBにテスト用のヘルパーメソッドを入れている。
> (例:環境変数の値を返すメソッド)
> ・(Cactusに倣って)EJBメソッドを外からテスト。
> ・TestServletの含まれるweb.xmlにEJBリファレンスと同じ設定を追加。
> etc.
と、かかれているということは、
http://jakarta.apache.org/cactus/howto_ejb_j2eeri.html
の存在はご存知と言うことですね。
僕は、この記述でも問題ないように思うんですけど・・・f(^_^;
> 【質問】
> 1. 商用・フリー問わずにEJBの単体テストができるソフトは存在するのか?
> 2. JUnitファミリーを使ってうまいことEJBの単体テストを行う手法、テクニック。
単体テストを行う手法と言うよりは、テストを行いやすいコードを書く
手法に近いかもしれませんが、、、
EJBに限らず、テストファーストを行う目的は、コードに対する品質に対する
自信をプログラマ自身が持つことだと思います。
もし、自分に自信が持てるのであれば、全てのコードをテストする必要は
ないと思うんです。
また、XP(を主題にはされていませんが)では受入れ試験も
リリース前には行わなければいけません。
受入れ試験で全体の流れに対するテストは行えばよいのだと思います。
とりあえず、
> ・SessionBeanから呼び出されるEntityBeanの単体テスト
> (SessionBeanから'java:comp/env/ejb/XxxEJB'で呼んでいる
> SessionBeanにEJBリファレンスとして登録)
と言うコードをテストする場合であれば、僕は以下のようなコードを
書きます。(コードは適当なんであんまり信じないでくださいね^^;)
ex)Foo->Barを呼び出す場合、
//FooBean.java
public class FooBean extends SessionBean {
public int func() {
Bar bar = getBar();
return bar.func();
}
Bar getBar() {
Context context = new InitialContext();
BarHome home = (BarHome)
PortableRemoteObject.narrow(ctx.lookup("java:comp/env/ejb/XxxEJB"),
BarHome.class);
return home.create(); }
}
//FooBeanExt.java
public class FooBean extends FooBean {
Bar getBar() {
return new Bar() {
public int func() {
return 10;
}
};
}
}
//FooBeanTest.java
public class FooBeanTest extends ServletTestCase {
public FooBeanTest(String name) {
super(name);
}
protected void setUp() {
Context ctx = new InitialContext();
FooHome home = (FooHome)
PortableRemoteObject.narrow(ctx.lookup("java:comp/ejb/Foo"),
FooHome.class);
foo = home.create();
}
public void testFunc() {
assertEquals(
"Barからの返却値を返却していません。", 10, foo.func());
}
private Foo foo;
}
とし、ejb-jar.xmlを以下のように書きます。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN'
'http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd'>
<ejb-jar>
<!-- snip -->
<enterprise-beans>
<session>
<home>ejb.foo.FooHome</home>
<remote>ejb.foo.Foo</remote>
<ejb-class>***.***.***@******@</ejb-class>
</session>
</enterprise-beans>
<!-- snip -->
</ejb-jar>
と書いて、antでearを作る際に、リリース版を作る時は、
@suffix@を空文字で置換し、テスト版を作る場合は、
@suffix@を"Ext"に置換するようなスクリプトを書きます。
僕は、このようなコードを書けばFooBeanに関しては「問題はない」と
判断できます(しちゃいますf(^_^;)。
FooBean.getBar()のテストは行われていないですが、そのコード内に
不良が入り込む余地があるかどうかなんです。
#そして、仮にあった場合でも受け入れ試験で環境が悪い
#と、言い切れるかどうかなんだと思います。
そうは言っても、まだgetBar()に不安があるかもしれませんね。
もしそうであれば、J2EE PatternのServiceLocatorを利用して、
FooBeanからHomeインタフェースの取得ロジックを追い出せば、
未テストのコード量はさらに減ることでしょう。
上記コードの肝は、
・Barを取得するコード自体には間違いが入り込む余地が無い。
(JNDIの設定はミスってるかもしれないが、それは単体で
検出できなくても良い)
・未テストのコードに不安があれば、EJB(等テストを行いにくいクラス)
から追い出して、テストしやすいクラスを別途切り出す。
・Barの振る舞いがどうあれ、Fooのロジックは検証できている。
(Barの振る舞いは、BarBeanTestでなされる(べきである))
と言う考えが根底にあると言うことです。
僕は、このコード構成を、「外部リソース追い出しパターン」と
呼んでます。
もしくは、「関連の切断パターン」とも呼んでいます。
EJBへの処理委譲を外部リソースへの依存と見るか、オブジェクトの関連と
見るかで呼び方は変えています。
#パターンと言うとおこがましいので、
#「イディオム」とすべきかもしれません。f(^_^;
EJBから見れば、別のEJBは外部リソースでしかないのですから、
外部リソースとの結合を弱めてやれば、
単体としてテストを行いやすくなると思います。
実際、このような形で作業を行っており、JUnit使用前と比較して
格段に品質は向上しております。
#当然、バグ0には至ってませんが・・・f(^_^;
後、思いつくのは、EJBをトランザクション制御と割り切って、
トランザクションに依らないロジックを別クラスに切り出す
と言うのも手だと思います。
#EJBをAdapter、切り出されたクラスをAdapteeとすれば、
#Adapteeのテストは、EJBコンテナ上で行わなくても良くなるはずです。
テストコードを書きやすいように、実コードを書いていけば、
テストコードの書きにくいコードはどんどん減らせます。
そして、それを繰り返していけば、
「うん、このコードはテストは無くても言いな」と言えるように
なると思います。
そして、気付けば、コードの冗長さや、長大なメソッドは
ナリを潜めていて、シンプルなコードになぜかなってたりします。
これらは、EJBのテストに限らないです。
長い上に、的外れだったらごめんなさい。
山根 英次(Eiji Yamane)
mailto:********@***.***
http://www.ne.jp/asahi/e/yamane/software/