2009-04-14 11 views
8

Questa domanda sorge durante il tentativo di scrivere casi di test. Foo è una classe all'interno della libreria di framework su cui non ho accesso all'origine.sovrascrive i metodi java final tramite riflessione o altri mezzi?

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

mie applicazioni sarà banco di prova

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

L'unità è in grado di inizializzare come non posso creare un oggetto Foo a causa di altre dipendenze. Il BarTest genera un puntatore nullo poiché il valore è null.

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

C'è un modo per utilizzare la reflection api per impostare getX() su non finale? o come dovrei andare sui test?

risposta

5

si potrebbe creare un altro metodo che si poteva ignorare nel test:

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

allora, si potrebbe ignorare doGetX in BarTest.

0

Se il caso di test dell'unità non è in grado di creare Foo a causa di altre dipendenze, questo potrebbe significare che non si sta effettuando il test dell'unità in un primo momento.

I test di unità hanno lo scopo di testare nelle stesse circostanze un codice di produzione, quindi suggerirei di ricreare lo stesso ambiente di produzione all'interno dei test. Altrimenti, i tuoi test non sarebbero completi.

+6

Questo non è il test unitario. I test unitari sono per unità di codice estremamente granulari. Quello che descrivi sembra più simile a test di integrazione o test funzionali. –

+0

Non necessariamente; Non conosco le circostanze specifiche in cui sta lavorando (ad esempio altre classi, DB, ecc.), Ma ci sono alcuni test che puoi fare con molte classi contemporaneamente - ad esempio, se stai usando una libreria esterna, è ok, supponiamo che stia funzionando bene e che usi tutti i suoi clases nei test. – Seb

+3

Sì, ci sono "alcuni test che puoi fare con molte classi contemporaneamente". Quelli non sono test unitari. Ancora utile, e importante da fare, ma non sono ** test ** unità. –

2

Seb è corretto, e solo per assicurarti di ottenere una risposta alla tua domanda, a meno di fare qualcosa nel codice nativo (e sono abbastanza sicuro che non funzionerebbe) o di modificare il bytecode della classe in fase di runtime, e creando la classe che sovrascrive il metodo in fase di runtime, non riesco a vedere un modo per modificare la "finalità" di un metodo. La riflessione non ti aiuterà qui.

+0

ti riferisci ad Aspect-oriented aka AOP? qualche idea se AOP può cambiare l'accesso a non-finale? – zeroin23

+1

Non stavo davvero intendendo AOP, non sono sicuro che sia in grado di farlo, stavo pensando di più sulla falsariga di http://jakarta.apache.org/bcel/. Tuttavia, se qualche cosa è difficile da fare, c'è probabilmente una ragione per questo ... e generalmente le cose difficili non dovrebbero essere fatte :-) – TofuBeer

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); 
    ... 
    } 
} 

quello che ho fatto è stato il precedente. è un po 'brutto ... James è decisamente molto meglio di questo ...

10

Come questo è stato uno dei migliori risultati per "override final method java" in google. Ho pensato di lasciare la mia soluzione. Questa classe mostra una soluzione semplice che utilizza un esempio di classe bagel e una libreria javassist gratuita:

//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; 
     } 
    } 
} 
Problemi correlati