2013-10-10 33 views
10

私は別のクラスの静的データを参照するクラスを単体テストしようとしています。私はこの静的なクラスを "使用"できませんが、明らかに複数のテストを実行することは問題になっています。だから私の質問はこれです。静的クラスを再初期化するためのjunitテストの方法はありますか?そのようにして、1回のテストは以前のテストで影響を受けていませんか?これを行うためのJavaで静的クラスを再初期化する方法はありますか?

換言すれば、いくつかの方法が:

Foo.setBar("Hello"); 

// Somehow reinitialize Foo 

String bar = Foo.getBar(); // Gets default value of bar rather than "Hello" 

残念ながら、私はFooのを変更することはできませんので、私はそれを使用してこだわっています。

編集私が少し単純すぎる私の例を作っ表示されます。実際のコードでは、 "Bar"はシステムプロパティによって設定され、内部静的変数に設定されます。一度起動すると変更できません。

+0

それはあなたが求めているものは明らかではありません。特定の時刻(?)にコードを実行するためのJUnit機能を探していますか?あるいは、一度初期化された外部 'Foo'クラスを変更することが可能かどうかを尋ねていますか? – chrylis

+0

もちろん、Fooを変更できるようにFooを変更することはできますが、あなたはそれを禁止されているようです。唯一の他の選択肢は、クラスを再ロードするためにプライベートクラスローダーとリフレクションを使用することです。しかし、 'Foo.getBar()'呼び出しを再作成する必要があります。 –

+1

[Java:静的クラスを "再起動する"方法](http://stackoverflow.com/questions/4631565/java-how-to-restart-a-static-class) –

答えて

4

少し汚れていましたが、私は反射を使用してこれを解決しました。静的なイニシャライザを再実行するのではなく、壊れやすいアプローチをとり、フィールドを既知の値に戻すユーティリティを作成しました。ここでは、静的フィールドを設定する方法のサンプルを示します。

5

あなたがPowerMockを使用する場合は、静的メソッドを模擬することができます - あなたは何をすべきかです。

2

あなたはそれはあなたが各テストでやりたい持っている静的クラスをモックする(Mockito付き)PowerMockまたはJMockitを使用することができます。

2

三つの提案、

  1. は、いくつかの既知の値に設定する@Beforeから静的メソッドを呼び出します。

  2. はリフレクション経由で値を設定するReflectionTestUtilsを使用してください。

  3. は、インスタンスメソッド/クラスの静的メソッドの呼び出しをラップインスタンスラッパークラスを持っているあなたのコードを更新します。ラッパーをモックしてテスト中のクラスに注入してください。

+0

(1)は、他のテストで値が変更されていない場合、つまり壊れやすい場合にのみ機能します。 – ash

0

私はすべてのインスタンスについての世話をする必要がありますinitdestroy静的メソッドでFactoryパターンを使用します。

のような何か:

public class FooFactory { 

    private static Foo mFoo = null; 

    public static Foo init(){ 

     if(mFoo == null){ 
      mFoo = new Foo(); 
     } 
     return mFoo; 
    } 

    public static void destroy(){ 
     if(mFoo != null){ 
      mFoo = null; 
     } 
    } 
} 

あたりそうunitest実行するのに十分な:

​​
0

技術的には、(テストのために必要とされるいくつかの他のクラスと一緒に)クラスをロードすることが可能ですクラスローダーからクラスに到達できないようにする必要があります。そのため、これを行うにはかなりのハッキングが必要です。通常のユニットテストでは可能です。その後、クラスローダーを削除し、次のテストのために再初期化することができます。各クラスローダーは、それによってロードされるすべてのクラスに対して独自の静的変数を持ちます。

あるいは、もう少しヘビーそれを行うと、各テストのための新しいJVMをフォーク。私は前にこれをやったことがあります(特に複雑な統合テストを行うのは便利ですが、システムプロパティを混乱させると便利です)。しかし、おそらくすべてのビルドで実行される単体テストではありません。

もちろん、これらの手法を組み合わせることもできます(ルートクラスローダーからクラスを取得しない場合) - クラスパス上に最小限の「ドライバ」を持つ新しいJVMをforkして、新しいクラスローダーを初期化します実行する各テストのための「通常の」クラスパス。

0

ここでは、静的初期化子を使用するユーティリティークラスを再ロードして、そのユーティリティーの初期化をテストする小さな例を示します。 ユーティリティは、システムプロパティを使用して静的最終値を初期化します。通常、この値は実行時には変更できません。 だからJUnitのテストが再静的初期化子を実行するために、クラスを再ロードする...

ユーティリティ:

public class Util { 
    private static final String VALUE; 

    static { 
     String value = System.getProperty("value"); 

     if (value != null) { 
      VALUE = value; 
     } else { 
      VALUE = "default"; 
     } 
    } 

    public static String getValue() { 
     return VALUE; 
    } 
} 

JUnitのテスト:

import static org.junit.Assert.assertEquals; 

import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 

import org.junit.Test; 

public class UtilTest { 

    private class MyClassLoader extends ClassLoader { 

     public Class<?> load() throws IOException { 
      InputStream is = MyClassLoader.class.getResourceAsStream("/Util.class"); 

      ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
      int b = -1; 

      while ((b = is.read()) > -1) { 
       baos.write(b); 
      } 

      return super.defineClass("Util", baos.toByteArray(), 0, baos.size()); 
     } 
    } 

    @Test 
    public void testGetValue() { 
     assertEquals("default", getValue()); 
     System.setProperty("value", "abc"); 
     assertEquals("abc", getValue()); 
    } 

    private String getValue() { 
     try { 
      MyClassLoader myClassLoader = new MyClassLoader(); 
      Class<?> clazz = myClassLoader.load(); 
      Method method = clazz.getMethod("getValue"); 
      Object result = method.invoke(clazz); 
      return (String) result; 
     } catch (IOException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { 
      throw new IllegalStateException("Error at 'getValue': " + e.getLocalizedMessage(), e); 
     } 
    } 
} 
関連する問題