2009-04-14 6 views
8

この疑問は、テストケースを書くときに発生します。 Fooはフレームワークライブラリ内のクラスであり、ソースのアクセス権を持っていません。リフレクションやその他の方法でJavaの最終的なメソッドをオーバーライドしますか?

public class Foo{ 
    public final Object getX(){ 
    ... 
    } 
} 

私のアプリケーション意志

public class Bar extends Foo{ 
    public int process(){ 
    Object value = getX(); 
    ... 
    } 
} 

ユニット・テスト・ケースは、私がために、他の依存関係にはFooオブジェクトを作成することはできませんようinitalizeすることができません。値がnullであるため、BarTestはnullポインタをスローします。

public class BarTest extends TestCase{ 
    public testProcess(){ 
    Bar bar = new Bar();   
    int result = bar.process(); 
    ... 
    } 
} 

反射APIを使用してgetX()を非最終に設定する方法はありますか?どのようにテストする必要がありますか?

答えて

5

あなたがあなたのテストでオーバーライドすることができます別の方法で作成することができます。その後、

public class Bar extends Foo { 
    protected Object doGetX() { 
    return getX(); 
    } 
    public int process(){ 
    Object value = doGetX(); 
    ... 
    } 
} 

を、あなたはBarTestでdoGetXをオーバーライドすることができます。

0

ユニットテストケースが他の依存関係のためにFooを作成できない場合は、最初にユニットテストを行っていないという兆候かもしれません。

ユニットテストは、生産コードが実行されるのと同じ状況下でテストされるため、テスト内に同じプロダクション環境を作り直すことをお勧めします。そうしないと、テストは完了しません。

+6

これは単位テストの対象ではありません。単体テストは非常にきめ細かなコード単位用です。あなたが説明していることは、統合テストや多分機能テストのように思えます。 –

+0

必ずしもそうではありません。彼が働いている特定の状況(つまり、他のクラス、DBなど)はわかりませんが、一度に多くのクラスで行うことができるテストがあります。たとえば、外部ライブラリを使用している場合などです。 OK、それが正常に動作していると仮定し、テストですべてのclasesを使用します。 – Seb

+3

ええ、「一度に多くのクラスでできるいくつかのテスト」があります。それらは単体テストではありません。まだ有用で、重要なことですが、** unit **のテストではありません。 –

2

Sebは正しいですが、実行時にクラスのバイトコードを変更するか、またはネイティブコードで何かを行うのに不足しています。実行時にメソッドをオーバーライドするクラスを作成すると、メソッドの「最終性」を変更する方法がわかりません。反射はここであなたを助けません。

+0

あなたはAspect-oriented aka AOPを参照していますか? AOPが非最終版へのアクセスを変更できるかどうか、任意のアイデア? – zeroin23

+1

私は本当にAOPを意味していませんでしたが、それができるかどうかわからない、私はhttp://jakarta.apache.org/bcel/のラインに沿ってもっと考えていました。しかし、もし何かするのが難しいのであれば、おそらく理由があるかもしれません...そして、一般的に難しいことはしないでください:-) – TofuBeer

0

getX()によって返された変数がfinalでない場合は、技術を使用することができますReflectionを通じてprivate変数の値を変更するためのWhat’s the best way of unit testing private methods?で説明しました。

+0

悲しいことに、それは最終的にマークされています...とにかく – zeroin23

0
public class Bar extends Foo{ 
    public int process(){ 
    Object value = getX(); 
    return process2(value); 
    } 
    public int process2(Object value){ 
    ... 
    } 
} 

public class BarTest extends TestCase{ 
    public testProcess(){ 
    Bar bar = new Bar(); 
    Mockobj mo = new Mockobj();  
    int result = bar.process2(mo); 
    ... 
    } 
} 

私は最終的には上記のものでした。それは少し醜いです...ジェームズの解決策はこれよりもはるかに優れています...

10

これはgoogleの "override final method java"のための最高の結果の1つでした。私は私の解決策を残すと思った。このクラスは、サンプルベーグルクラスと自由に使用できるjavassistライブラリを使用した単純なソリューションを示しています。

//This class shows how you can override a final method of a super class using the Javassist's bytecode toolkit 
//The library can be found here: http://jboss-javassist.github.io/javassist/ 
// 
//The basic idea is that you get the super class and reset the modifiers so the modifiers of the method don't include final. 
//Then you add in a new method to the sub class which overrides the now non final method of the super class. 
// 
//The only "catch" is you have to do the class manipulation before any calls to the class happen in your code. So put the 
//manipulation as early in your code as you can. 

package packagename; 

import javassist.ClassPool; 
import javassist.CtClass; 
import javassist.CtMethod; 
import javassist.CtNewMethod; 
import javassist.Modifier; 

public class TestCt { 

    public static void main(String[] args) { 
     try 
     { 
      // get the super class 
      CtClass bagel = ClassPool.getDefault().get("packagename.TestCt$Bagel"); 

      // get the method you want to override 
      CtMethod originalMethod = bagel.getDeclaredMethod("getDescription"); 

      // set the modifier. This will remove the 'final' modifier from the method. 
      // If for whatever reason you needed more than one modifier just add them together 
      originalMethod.setModifiers(Modifier.PUBLIC); 

      // save the changes to the super class 
      bagel.toClass(); 

      // get the subclass 
      CtClass bagelsolver = ClassPool.getDefault().get("packagename.TestCt$BagelWithOptions"); 

      // create the method that will override the super class's method 
      CtMethod overrideMethod = CtNewMethod.make("public String getDescription() { return super.getDescription() + \" with \" + getOptions(); }", bagelsolver); 

      // add the new method to the sub class 
      bagelsolver.addMethod(overrideMethod); 

      // save the changes to the sub class 
      bagelsolver.toClass(); 
     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     } 

     BagelWithOptions myBagel = new BagelWithOptions(); 

     myBagel.setOptions("cheese, bacon and eggs"); 

     System.out.println("My bagel is: " + myBagel.getDescription()); 
    } 

    public static class Bagel { 
     public final String getDescription() { 
      return "a plain bagel"; 
     } 
    } 

    public static class BagelWithOptions extends Bagel { 
     String options; 

     public BagelWithOptions() { 
      options = "nothing else"; 
     } 

     public void setOptions(String options) { 
      this.options = options; 
     } 

     public String getOptions() { 
      return options; 
     } 
    } 
} 
関連する問題