2009-04-14 78 views
8

嘗試編寫測試用例時出現此問題。 Foo是框架庫中的一個類,我沒有源訪問權限。通過反射或其他方式重寫java final方法?

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

我的應用程序將

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

的單元測試用例是無法initalize因爲我不能創建一個Foo對象,由於其他的依賴。由於值爲null,BarTest會拋出一個空指針。

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

這不是單元測試的目的。單元測試用於非常精細的代碼單元。你所描述的聽起來更像集成測試或可能的功能測試。 – 2009-04-14 16:40:07

+0

不一定;我不知道他工作的具體情況(即其他類,數據庫等),但是您可以同時對多個類進行某些測試 - 例如,如果您使用外部庫,它是好吧,假設它工作正常,並在你的測試中使用它的所有類。 – Seb 2009-04-14 16:47:35

+3

是的,有「你可以同時做很多課程的某些測試」。那些不是單元測試。仍然有用,並且很重要,但它們不是**單元**測試。 – 2009-04-14 17:05:44

2

Seb是正確的,只是爲了確保你得到了你的問題的答案,在本機代碼中執行某些操作(並且我確信這樣做不行),或者在運行時修改類的字節碼,以及創建一個在運行時重寫方法的類,我看不到一種方法來改變方法的「最終性」。反思在這裏不會幫助你。

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

因爲這是在谷歌「重寫最終方法Java」的最高結果之一。我想我會離開我的解決方案。這個類使用示例百吉圈類和免費使用的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; 
     } 
    } 
} 
相關問題