2010-06-08 25 views
14

Così ho iniziato a unit test il layout per il seguente frammento di codice:Unità Test Architettura Domanda

public interface MyInterface { 
    void MyInterfaceMethod1(); 
    void MyInterfaceMethod2(); 
} 

public class MyImplementation1 implements MyInterface { 
    void MyInterfaceMethod1() { 
    // do something 
    } 

    void MyInterfaceMethod2() { 
    // do something else 
    } 

    void SubRoutineP() { 
    // other functionality specific to this implementation 
    } 
} 

public class MyImplementation2 implements MyInterface { 
    void MyInterfaceMethod1() { 
    // do a 3rd thing 
    } 

    void MyInterfaceMethod2() { 
    // do something completely different 
    } 

    void SubRoutineQ() { 
    // other functionality specific to this implementation 
    } 
} 

con diverse implementazioni e l'aspettativa di più a venire.

Il mio primo pensiero è stato quello di salvare me stesso test di unità ri-scrittura di tempo con qualcosa di simile:

public abstract class MyInterfaceTester { 
    protected MyInterface m_object; 

    @Setup 
    public void setUp() { 
    m_object = getTestedImplementation(); 
    } 

    public abstract MyInterface getTestedImplementation(); 

    @Test 
    public void testMyInterfaceMethod1() { 
    // use m_object to run tests 
    } 

    @Test 
    public void testMyInterfaceMethod2() { 
    // use m_object to run tests 
    } 
} 

che ho potuto poi sottoclasse facilmente testare l'implementazione specifici metodi aggiuntivi in ​​questo modo:

public class MyImplementation1Tester extends MyInterfaceTester { 
    public MyInterface getTestedImplementation() { 
    return new MyImplementation1(); 
    } 

    @Test 
    public void testSubRoutineP() { 
    // use m_object to run tests 
    } 
} 

e similmente per l'impianto 2 in poi.

Quindi la mia domanda è davvero: c'è qualche ragione per non farlo? A JUnit sembra piacere e va bene per i miei bisogni, ma non ho mai visto nulla di simile in nessuna delle unità che testano libri ed esempi che ho letto.

C'è qualche pratica migliore che sto inconsapevolmente violando? Mi sto preparando per il dolore per la strada? C'è semplicemente un modo migliore là fuori che non ho considerato?

Grazie per qualsiasi aiuto.

risposta

17

c'è qualche ragione per non farlo?

No. Fatelo. I test sono classi per esattamente questo motivo.

Non ho mai visto nulla di simile in nessuna delle unità di test di libri ed esempi che ho letto.

Continua a leggere. Le presentazioni non coprono questo.

C'è qualche pratica migliore che sto inconsapevolmente violando?

No.

Am I impostazione me up per mal di cuore per la strada?

No.

Alcune persone diventano nervosi su "prove fragili". Qui puoi trovare alcune domande alla ricerca di modi per fare in modo che una modifica al software non porti anche a modifiche ai test. A lungo termine, cercare di creare test "robusti" è sciocco. Si desidera test scritti in modo che ogni piccola modifica al livello di interfaccia visibile del software richieda la riscrittura del test.

Si desidera test in modo che le modifiche interne invisibili non richiedano la riscrittura del test.

L'utilizzo di classi e sottoclassi è ortogonale a tali considerazioni.

C'è semplicemente un modo migliore là fuori che non ho considerato?

N. orientamento dell'oggetto è il punto. I test sono una classe proprio per questo motivo.

5

Pur appoggiando SLott 100% Vorrei anche prendere in considerazione JUnit parametrizzata test invece di gerarchia delle classi di test per questo:

@RunWith(Parameterized.class) 
public class MyInterfaceTester { 
    private MyInterface m_object; 

    public void MyInterfaceTester(MyInterface object) { 
    m_object = object; 
    } 

    @Parameters 
    public static Collection<Object[]> data() { 
    List<Object[]> list = new ArrayList<Object[]>(); 

    list.add(new Object[]{new MyImplementation1()}); 
    list.add(new Object[]{new MyImplementation2()}); 

    return list; 
    } 

    @Test 
    public void testMyInterfaceMethod1() { 
    // use m_object to run tests 
    } 

    @Test 
    public void testMyInterfaceMethod2() { 
    // use m_object to run tests 
    } 
} 

Non c'è bisogno di gerarchia di classe di test: basta aggiungere nuova implementazione aggiungendo un altro elemento della lista in data metodo.

+0

Non conoscevo questa funzione, grazie per la condivisione. Maggiori informazioni e un esempio operativo qui: http://www.devx.com/Java/Article/31983/0/page/3 per chiunque sia interessato. –

1

Se si sta effettuando la stessa configurazione e si abbattono in ogni classe di test, allora quello che si sta facendo va bene, ma trovo che nella pratica questo non è quasi mai il caso. In effetti, il più delle volte disporre di un metodo di installazione che istanzia dati di test, come hai qui, non è nemmeno quello che vuoi. Invece, all'interno di una classe di test si installa qualsiasi infrastruttura e ogni metodo di test imposta la propria istanza di un oggetto per testarne alcuni aspetti.

+0

È vero, spostando la chiamata getTestedImplementation() alla prima riga di ogni test e inserendo il risultato in un locale potrebbe essere ciò che voglio in linea. Probabilmente è più sicuro iniziare con un nuovo oggetto. Al momento, però, non esiste uno stato nelle classi di implementazione, quindi è necessario crearne solo una volta. –