Java-ユニットテスト中にリソースファイルパスをテストファイルにスワップする方法


8

いくつかの設定を持つリソースファイルがあります。このファイルから設定をロードするResourceLoaderクラスがあります。このクラスは現在、熱心にインスタンス化されたシングルトンクラスです。このクラスがロードされるとすぐに、ファイル(別のクラスの定数フィールドとして保存されているファイルパス)から設定を読み取ります。これらの設定の一部は、単体テストには適していません。たとえば、このファイルにはスレッドのスリープ時間があり、これは製品コードでは数時間になる場合がありますが、単体テストでは数ミリ秒にしたいと考えています。そのため、値のセットが異なる別のテストリソースファイルがあります。私の質問は、単体テスト中にメインのリソースファイルをこのテストファイルと交換するにはどうすればよいですか?プロジェクトはMavenプロジェクトであり、テストフレームワークとしてtestngを使用しています。これらは私のアプローチの一部です

  1. @BeforeSuiteを使用してFilePath定数変数を変更し、テストファイルを指すようにし、@ AfterSuiteを使用して元のファイルを指すようにします。これは機能しているようですが、ResourceLoaderクラスは熱心にインスタンス化されているため、ResourceLoaderクラスがロードされる前に@BeforeSuiteメソッドが常に実行される保証はなく、ファイルパスが変更される前に古いプロパティがロードされる可能性があります。ほとんどのコンパイラーは、必要な場合にのみクラスをロードしますが、これがJava仕様の要件であるかどうかはわかりません。したがって、理論的には、これはすべてのJavaコンパイラで機能するとは限りません。

  2. リソースファイルのパスをコマンドライン引数として渡します。pomのsurefire構成でコマンドライン引数としてテストリソースファイルパスを追加できます。これは少し過剰に見えます。

  3. 1.のアプローチを使用して、ResourceLoaderを遅延インスタンス化します。これにより、ResourceLoader.getInstance()。getProperty(..)への最初の呼び出しの前に@BeforeMethodが呼び出された場合、ResourceLoaderが正しいファイルをロードすることが保証されます。これは最初の2つのアプローチよりも優れているようですが、シングルトンクラスを遅延インスタンス化すると、列挙型などの単純なパターンを使用できないため、醜くなります(熱心なインスタンス化の場合のように)。

これは一般的なシナリオのようですが、最も一般的な方法は何ですか?


より良い構成ライブラリを使用するようにアプリケーションを作り直すことはオプションですか?
するThorbjörnRavnアンデルセン

はい、これを行うための標準的な方法を知りたいだけです。私はアプリケーションを作り直すことを開いています。
マルコカイン

唯一の標準的な方法は、環境やシステムプロパティを使用することです。たとえばテストでは、コマンドラインからの提供とオーバーライドの両方が難しいため、通常は制限が多すぎます。必要なもの、およびいくつかのユースケースでアプリケーションを構成できるようにする方法について、質問に情報を追加することをお勧めします。構成値を必要とするクラスのコンストラクターで構成値を渡すことを検討したい場合があります。
するThorbjörnRavnアンデルセン

回答:


7

積極的または遅延的にインスタンス化されたすべてのシングルトンはアンチパターンです。シングルトンをモックする簡単な方法がないため、シングルトンを使用すると単体テストが難しくなります。

モック静的メソッド

回避策は、PowerMockを使用して、シングルトンインスタンスを返す静的メソッドモックすることです。

依存性注入を使用する

より良い解決策は、依存性注入を使用することです。すでに依存性注入フレームワーク(例えば春、CDI)を使用している場合は、作成するコードをリファクタリングマネージドBeanをしてスコープシングルトンResourceLoader

依存関係注入フレームワークを使用しない場合、簡単なリファクタリングは、シングルトンを使用してすべてのクラスを変更することResourceLoaderです。

public class MyService {

  public MyService() {
    this(ResourceLoader.getInstance());
  }

  public MyService(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }
}

そして、ユニットテストではモッキートResourceLoaderを使用してモック

ResourceLoader resourceLoader = mock(ResourceLoader.class);
when(ResourceLoader.getProperty("my-property")).thenReturn("10");
MyService myService = new MyService(resourceLoader);

外部化構成

別の方法は、テスト設定を含むファイルをに配置することですsrc/test/resources。に設定を保存するsrc/main/resources/application.propertiesと、ファイルsrc/test/resources/application.propertiesによって上書きされます。

また、JARにパッケージされていないファイルに構成を外部化することもお勧めします。このように、ファイルsrc/main/resources/application.propertiesにはデフォルトのプロパティが含まれ、コマンドラインパラメータを使用して渡されたファイルはこれらのプロパティをオーバーライドします。そのため、テストプロパティを含むファイルもコマンドラインパラメータとして渡されます。Springが外部化された構成を処理する方法を参照してください。

Javaシステムプロパティを使用する

さらに簡単な方法は、メソッドのシステムプロパティでデフォルトプロパティをオーバーライドできるようにし、ResourceLoader.getInstance().getProperty()この方法でテストプロパティを渡すことです。

public String getProperty(String name) {
  // defaultProperties are loaded from a file on a file system:
  // defaultProperties.load(new FileInputStream(new File(filePath)));
  // or from a file in the classpath:
  // defaultProperties.load(ResourceLoader.class.getResourceAsStream(filePath));
  return System.getProperty(name, defaultProperties.get(name));
}

0

あなたがjUnitにいるかどうかを確認してください

ランタイムでjUnitが実行されているかどうかを確認してから、パスを交換することもできます。これは次のように機能します(未テスト)。

public static funktionToTest() {
    StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
    List<StackTraceElement> list = Arrays.asList(stackTraceElements);
    String path = "/origin/path/to/your/file"; // here can you set the default path to your rescource
    for (StackTraceElement stackTraceElement : list) {
        if (stackTraceElement.getClassName().startsWith("org.junit.")) {
            path = "/path/only/in/jUnit/test"; // and here the normal path
        }           
    }
    //do what you want with the path
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.