4

メッセージを JMS キューに送信し、そこからの応答を待つ EJB があります。EJB をテストしたいのですが、OpenEJB を使用して EJB の JUnit テストを行うのは簡単です。しかし問題は、この EJB が JMS 応答を待って処理を続行することです。

junit コードでメッセージを送信することはできますが、EJB がまだ進行中であるため、EJB が完了する前にメッセージを実行することはできません。

2 番目の解決策は、MDB を初期化して EJB からの JMS メッセージをリッスンして返信できるようにすることですが、問題は MDB が src\main\java にある必要があり、src\test\java にあることができないことです。問題は、これは単なるテスト コードであり、運用環境にパッケージ化するべきではないということです。(Mavenを使用しています)

それともモックオブジェクトを使うべきですか?

4

5 に答える 5

6

あなたは正しい軌道に乗っています。これを処理する方法はいくつかあります。OpenEJB と Maven を使用した単体テストのヒントをいくつか紹介します。

テスト豆

あらゆる種類の EJB やその他のテスト ユーティリティを作成し、デプロイすることができます。必要なのは、ejb-jar.xml次のようなテスト コードの だけです。

  • src/main/resources/ejb-jar.xml (通常のもの)

  • src/test/resources/ejb-jar.xml (テスト Bean)

いつものように、ejb-jar.xmlファイルには含まれているだけで、それ以上のものは必要<ejb-jar/>ありません。その存在は、クラスパスのその部分を検査し、Bean をスキャンするように OpenEJB に指示するだけです。クラスパス全体をスキャンするのは非常に遅いため、これは速度を上げるための慣習です。

テストケース インジェクション

上記src/test/resources/ejb-jar.xmlを使用すると、そのテスト専用 MDB を非常に簡単に追加して、TestCase が必要とする方法でリクエストを処理するようにセットアップすることができます。しかし、src/test/resources/ejb-jar.xml他の興味深い機能も利用できます。

TestCase必要なJMSリソースへの参照を宣言し、それらを注入することで、それ自体でそれを行うことができます。

import org.apache.openejb.api.LocalClient;

@LocalClient
public class ChatBeanTest extends TestCase {

    @Resource
    private ConnectionFactory connectionFactory;

    @Resource(name = "QuestionBean")
    private Queue questionQueue;

    @Resource(name = "AnswerQueue")
    private Queue answerQueue;

    @EJB
    private MyBean myBean;


    @Override
    protected void setUp() throws Exception {
        Properties p = new Properties();
        p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
        InitialContext initialContext = new InitialContext(p);

        initialContext.bind("inject", this); // here's the magic!
    }
}

これで、テストケース自体が JMS メッセージに応答できるようになるまであと 1 スレッドになりました。単一のメッセージを読み取り、必要な応答を送信して終了する小さなランナブルを起動できます。

たぶん次のようなものです:

public void test() throws Exception {

    final Thread thread = new Thread() {
        @Override
        public void run() {
            try {
                final Connection connection = connectionFactory.createConnection();

                connection.start();

                final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

                final MessageConsumer incoming = session.createConsumer(requestQueue);
                final String text = ((TextMessage) incoming.receive(1000)).getText();

                final MessageProducer outgoing = session.createProducer(responseQueue);
                outgoing.send(session.createTextMessage("Hello World!"));

            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    };
    thread.setDaemon(true);
    thread.start();

    myBean.doThatThing();

    // asserts here...
}

見る

代替記述子

MDB ソリューションを使用する必要があり、すべてのテストではなく 1 つのテストのみで有効にしたい場合は、特別なsrc/test/resources/mockmdb.ejb-jar.xmlファイルで定義し、必要な特定のテスト ケースで有効にすることができます。

その記述子と代替記述子のさまざまなオプションを有効にする方法の詳細については、このドキュメントを参照してください。

于 2011-09-16T17:37:42.973 に答える
2

これにはモックを使用する必要があると思います。実際の JMS サーバーにメッセージを送信したり、リッスンしたり、返信したりしている場合は、単体テスト以外のことを行っています。それが何と呼ばれるべきかについての議論に入るつもりはありませんが、ユニットテストがライブデータベースやメッセージキューなどと通信するべきではないということは、広く受け入れられていると思います.

于 2011-09-16T14:30:27.240 に答える
2

私があなたの質問を正しく理解していれば、EJB が JMS メッセージを送信してから応答を待つのは悪い設計です。実際、EJB の全体的な考え方と矛盾しています。

JMS メッセージを送信した後、それを忘れます。メッセージを受信するための MDB があります。EJB が応答に依存している場合は、JMS を使用するのではなく、別の EJB を使用します。

送信をテストするには、JMS クラスをモックし、MDB を個別にテストします。

EJB は同期タスク用に設計されており、JMS は非同期タスク用に設計されています。外部システムと非同期通信を行う必要がある場合は、その後でシステムを設計し、適切な非同期フローを実行することをお勧めします。JMS 応答を待っている EJB はせいぜい醜いハックであり、システム設計に何のメリットもありません。

于 2011-09-16T15:16:58.640 に答える
1

デビッドの答えをありがとう、それは私が欲しいものです。単体テストは、JMS サーバーなどの他の外部リソースに依存すべきではないことはわかっています。しかし、Maven + OpenEJB を使用すれば、テスト コードをクローズド環境に置くことができます。特にリファクタリングが容易ではない一部の古いプログラムの場合、外部リソースの依存関係を自動的にテストするのに役立ちます。

また、initialContext.bind("inject", this) に次のエラー メッセージが表示される場合

クラスに @org.apache.openejb.api.LocalClient のアノテーションが付けられ、正常に検出およびデプロイされたことを確認します。

1 つの参照はhttp://openejb.apache.org/3.0/local-client-injection.html ですが、「openejb.tempclassloader.skip=annotations」を追加しても機能しません。このドキュメントOpenEJB Local Client Injection Failsを確認してください。すでにパッチがあり、OpenEJB 3.1.5 または 4.0 で修正されると思います。

于 2011-09-17T15:04:16.353 に答える
0

また、MDB 内のロジックを実際に別のクラスに分割することがベスト プラクティスであることもわかりました。これにより、ビジネス ロジックが MDB 内に存在することから分離され、ロジックを複数の方法 (MDB、EJB、Web サービス、POJO など) として公開できます。また、プロトコル (この場合は JMS) をテストする必要なく、ビジネス ロジックをより簡単にテストできます。

JMS のテストに関しては、モックの方が適している場合があります。または、本当に「コンテナ内」でプロトコルをテストする必要がある場合は、JBoss Microcontainer などを使用してみてください (これは、Seam などの JBoss プロジェクトのいくつかと一緒にパッケージ化できると思います)。次に、EJB や JMS などをテストするためのミニコンテナを起動できます。

しかし、全体として、絶対に必要でない限り、コンテナーを必要としないことを避けるのが最善です。そのため、(モックを使用しない場合でも) ビジネス ロジックを実装ロジックから分離することをお勧めします。

于 2011-09-16T15:36:00.777 に答える