7

次のように、Java ServiceLoader 仕様の一部として Hibernate Integrator を定義する Web アプリケーションがあります。

src/main/resources/META-INF/services/org.hibernate.integrator.spi.Integrator

  # Define integrators that should be instantiated by the ServiceLoader
  org.emmerich.MyIntegrator

これは、こちらの Hibernate ガイドに従って行われます。

私の問題は、単体テストを実行しようとすると、メインのインテグレーター記述子がまだ解析され、インスタンス化されていることです。これは、単体テストでアプリケーションの大部分をモックしているため、インテグレーターが実行しようとするとエラーが発生し、テストが失敗することを意味します。

テスト リソースで同じファイルを定義しました。

src/test/resources/META-INF/services/org.hibernate.integrator.spi.Integrator

  # Empty file to try and overwrite the main deployment description.

しかし、代わりに、テスト ファイルとメイン インテグレーター ファイルの両方が解析されていることがわかりました。

テスト リソースがメイン リソースを上書きし、メイン リソースが陳腐化することを期待していましたが、そうはなりませんでした。test-classes両方のファイルがクラスパス上にあるため (クラスパスにとの両方を配置する Surefire-plugin を使用して、Maven を介してテストを実行していますclasses)。と同様のことが起こりpersistence.xmlます。

私の単体テスト環境では、これらの Bean の構築をできるだけ手動で制御したいので、インテグレーターをインスタンス化したくありません。私が実行単位をテストしていることを考えると、テストの実行に影響を与える可能性のあるインテグレーターなどの追加の Bean が横たわっていることは望ましくありません。これは、単体テスト中の完全に正当な要件だと思います。ただし、主要なリソースは引き続き によって解析されますがServiceLoader、これは不可能です。

私がやろうとしている解決策は、persistence.xmlここに投稿された解決策に基づいています。

Maven でのテスト用に JPA を構成する方法

ServiceLoader私の質問は、特にファイルのコンテキストで、名前の変更を強制するよりも、単体テスト中に処理されないように主なリソースを除外するより良い方法があるかどうかです。

もう少しうまく要約するには:

クラスパスに同じサービス インターフェイスにちなんで名付けられた 2 つのファイルがある場合はどうなるでしょうか。私には、両方のファイル内のすべてのサービスがインスタンス化されているようです。上書きはないようです。

4

1 に答える 1

1

興味のある人のために、もう少し調査すると、これは実際には Hibernate の問題ではなくServiceLoader、ファイルへのロード方法にあるという事実につながります。APIから:

特定の具象プロバイダ クラスが複数の構成ファイルで指定されている場合、または同じ構成ファイルで複数回指定されている場合、重複は無視されます。

残念ながら、これは具体的なクラスにのみ関係します。したがって、同じファイルの 2 つのコピーを次の形式で指定すると、次のようになります。

org.emmerich.MyServiceInterface

  org.emmerich.MyServiceImpl

thenMyServiceImplは一度だけインスタンス化されます。ただし、2 つの異なる実装クラスを持つ 2 つのファイルを指定すると、両方の実装がインスタンス化されます。

これは、サービスがインスタンス化される場所を少し細かく制御したい単体テストにはあまり適していません。その背後にある設計上の決定についてはよくわかりませんが、Hibernate でこれを使用すると、単体テストが難しくなります。

とにかく、私が思いついた解決策は、メインリソースのオーバーライドを、ファイルのように私が制御できるファイルに委任することでしたproperties。サービス内で、そのプロパティ ファイルのフラグが true か false かを確認します。私の主なリソースでは、それは本当です。私のテストでは、それは偽です。プロパティを検索すると、クラスパスの最初のファイルにヒットするため、テスト リソースが取得されることがわかります。私の構造は次のようになります。

src
  main
    resources
      META-INF
        services
          org.hibernate.integrator.spi.Integrator # has line MyIntegrator
    application.properties     # shouldIntegrate=true 
  test
    resources
      application.properties   # shouldIntegrate=false

そしてでMyIntegrator

public class MyIntegrator implements Integrator {

  @Override
  public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
    if(shouldIntegrate()) {
      // do integration
    }
  }

  private boolean shouldIntegrate() {
    // read Properties file from classpath
    // getClass().getClassLoader().getResourceAsStream("application.properties")
    // return value of "shouldIntegrate"
  }

テスト環境で実行すると、プロパティ ファイルのルックアップがテスト リソースを指します。テスト環境の外では、メイン リソースを指します。

于 2013-04-19T16:11:02.480 に答える