14

私は JAX-RS と Jersey の学習を楽しんでいますが、次のような DAO の注入が必要な単純なリソースをテストしようとして障害にぶつかりました。

@Stateless
@Path("simple")
public class SimpleResource {

    @PersistenceContext
    private EntityManager em;

// @GET, etc...

}

(より抽象化された DAO パターンに移行しますが、問題は同じです。つまり、@EJB DAO をどのように注入すればよいでしょうか?)

単体テストでは、Jersey を web.xml で構成する組み込みの Jetty サーバーを使用しています。リソースのライフサイクルにフックして、モック EntityManager を挿入できるようにしたいと考えていますが、明確な答えが見つかりませんでした。いろいろ検索した結果。手伝ってくれますか?私が遭遇したいくつかの可能な方向:

1) コードで JNDI コンテキスト ルックアップを使用して DAO Bean を取得し、テストでモック オブジェクトを登録します。

@EJB または @PersistenceContext の代わりに、リソースのコンストラクターで次のようなものを使用します。

  theDAO = (DAOImpl) new InitialContext().lookup("java:global/EJB/DAOImpl");

ただし、それは私のテスト環境が JNDI をサポートする必要があることを意味し、Jetty でこれを行うにはおそらく多少の苦痛が伴います。さらに、クリーン アノテーション アプローチを使用しません。

2) メソッド注入を使用します。

メソッドに注入して、インスタンス化後の DAO を設定できるようにします。

@PersistenceContext(name = "persistence/pu00")
public void setPersistenceUnit00(final EntityManager em) {
    em00 = em;
}

また

private MyEjbInterface myEjb;
@EJB(mappedName="ejb/MyEjb")
public void setMyEjb(MyEjb myEjb) {
    this.myEjb = myEjb;
}

ただし、これを行うには、Jersey でインスタンス化されたインスタンス (SimpleResource など) が必要です。どうすればそれを取得できますか?

3) リフレクションを使用します。

DIY インジェクションのようなもので、次のようなものです。

public static void setPrivateField(Class<? extends Object> instanceFieldClass, Object instance, String fieldName, Object fieldValue) {
    Field setId = instanceFieldClass.getDeclaredField(fieldName);
    setId.setAccessible(true);
    setId.set(instance, fieldValue);
}

ここでも、Jersey でインスタンス化されたインスタンスが必要です。

4) インジェクション プロバイダーを使用します。

これがどのように機能するかについてはまだ大ざっぱですが、ジャージーはカスタマイズされた注入可能な注釈を定義する手段を提供しているようです。

    @Provider
    public class EJBProvider implements InjectableProvider<EJB, Type> {

    public ComponentScope getScope() {
    return ComponentScope.Singleton;
    }

    public Injectable getInjectable(ComponentContext cc, EJB ejb, Type t) {
    if (!(t instanceof Class)) {
        return null;
    }
    try {
        Class c = (Class) t;
        Context ic = new InitialContext();
        final Object o = ic.lookup(c.getName());
        return new Injectable<Object>() {
        public Object getValue() {
            return o;
        }
        };
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
    }
}

ヘルパー クラスを使用したバリエーション:

Server server = new Server(8080);
Context root = new Context(server,"/",Context.SESSIONS);

ResourceConfig rc = new PackagesResourceConfig("edu.mit.senseable.livesingapore.platform.restws.representations");
rc.getSingletons().add(new SingletonTypeInjectableProvider<javax.ws.rs.core.Context, Myobj>(Myobj.class, new Myobj(12,13)){});

root.addServlet(new ServletHolder(new ServletContainer(rc)), "/");
server.start();

この使用で:

@Path("/helloworld")
public class HelloWorldResource {
    @Context Myobj myClass;
    ....
}

これは @EJB または @PersistenceContext で実行可能ですか?

5) javax.ws.rs.core.Application を拡張します。

これについては大ざっぱですが、

@javax.ws.rs.ApplicationPath("application")
public class InjectionApplication extends javax.ws.rs.core.Application {

  private Set<Object> singletons = new HashSet<Object>();
  private Set<Class<?>> classes = new HashSet<Class<?>>();

  public InjectionApplication() {
    // no instance is created, just class is listed
    classes.add(BookResource.class);
  }

  @Override
  public Set<Class<?>> getClasses() {
    return classes;
  }

  @Override
  public Set<Object> getSingletons() {
    return singletons;
  }
}

6) ServletContainer を拡張します。

InjectableProvider を使用する古いスタイルですか? より複雑に見えます:

public class ServletAdapter extends ServletContainer {

@Override
protected void configure(ServletConfig servletConfig, ResourceConfig rc, WebApplication wa) {
    super.configure(servletConfig, rc, wa);

    rc.getSingletons().add(new InjectableProvider<Resource, Type>() {

        public ComponentScope getScope() {
            return ComponentScope.Singleton;
        }

        public Injectable<Object> getInjectable(ComponentContext ic, Resource r, Type c) {
            final Holder value = new Holder();

                Context ctx = new InitialContext();
                try {
                    value.value = ctx.lookup(r.name());
                } catch (NamingException ex) {

                    value.value = ctx.lookup("java:comp/env/" + r.name());
                }

            return new Injectable<Object>() {
                public Object getValue() {
                    return value.value;
                }
            };
        }
    });
}
}  

7) 組み込み EJB コンテナーを使用します。

例: http://openejb.apache.org。これはかなり重いので、動作させるのは面倒だと思います。(実際、私が「Jetty + Jersey」ルートにたどり着いたのは、GlassFish Embedded のセキュリティ ログインに関するバグでした。JBoss AS などの他の Java EE 6 アプリケーション コンテナも調べましたが、いずれも組み込みモードで問題があり、ユーザーが制限されていました。コミュニティ サポート)。

8) Spring や Guice などのサードパーティの IoC ライブラリを使用します。

Spring は、この種の問題 (単体テスト時にモックを挿入する) を解決するために一般的に使用されているようですが、別の大きな API セットを学習する必要がないようにしたかったので、純粋な Java EE は十分な課題でした! しかし、それが最善の解決策である場合、私はゲームです。Spring や Guice についてはまだ詳しく調べていません。

これらのいずれかをうまく使用しましたか? 他に好きなソリューションはありますか?これに関するあなたのアドバイスを本当に楽しみにしています。前もって感謝します -- マット

4

2 に答える 2

1

Netbeans を使用しているので、これを試してみてください。

組み込み EJB コンテナを使用してエンタープライズ アプリケーションをテストする

このチュートリアルでは、組み込みの Glassfish コンテナーを使用し、EntityManager をカプセル化する EJB を注入します (最初のオプションで説明したものと同様です)。

于 2013-02-01T08:38:28.833 に答える
0

埋め込まれた Jetty コンテナーの内部のみが必要な場合、EntityManagerそもそもなぜインジェクションを使用するのでしょうか? クラスパスに JPA 実装 (eclipselink や hibernate など) の 1 つを配置し、リソースのローカル永続化ユニットを構成してから、次のように取得できます。

EntityManagerFactory emf = Persistence.createEntityManagerFactory("your unit name");
EntityManager em = emf.createEntityManager();

@EJBJAX-RS クラスをテストする目的で、あなたのように動作する何か (おそらく静的 DAO ファクトリ?) があれば十分です。

単体テストをできる限り Java EE 環境に近づけたい場合は、Arquillian (http://www.jboss.org/arquillian.html) で実行することを検討してください。Java EE コンテナーで直接テストを実行します。簡単で、優れたドキュメントがあります。

于 2012-11-02T14:08:47.180 に答える