2011-09-27 9 views
61

Sono nuovo al test Java con JUnit. Devo lavorare con Java e mi piacerebbe utilizzare i test unitari.Come testare la classe astratta in Java con JUnit?

Il mio problema è: ho una classe astratta con alcuni metodi astratti. Ma ci sono alcuni metodi che non sono astratti. Come posso testare questa classe con JUnit? Esempio di codice (molto semplice):

abstract class Car { 

    public Car(int speed, int fuel) { 
     this.speed = speed; 
     this.fuel = fuel; 
    } 

    private int speed; 
    private int fuel; 

    abstract void drive(); 

    public int getSpeed() { 
     return this.speed; 
    } 

    public int getFuel() { 
     return this.fuel; 
    } 
} 

voglio testare getSpeed() e getFuel() funzioni.

La domanda simile a questo problema è here, ma non sta utilizzando JUnit.

Nella sezione Domande frequenti JUnit, ho trovato this link, ma non capisco cosa voglia dire l'autore con questo esempio. Cosa significa questa riga di codice?

public abstract Source getSource() ; 
+3

Vedere http://stackoverflow.com/questions/1087339/using-mockito-to-test-abstract-classes per due soluzioni che utilizzano Mockito. – ddso

+0

C'è qualche vantaggio nell'apprendere un altro framework per i test? Mockito è solo un'estensione di jUnit, o un progetto completamente diverso? – vasco

+0

Mockito non sostituisce JUnit. Come altri framework di simulazione, è utilizzato in aggiunta a un framework di test delle unità e ti aiuta a creare oggetti mock da utilizzare nei tuoi casi di test. – ddso

risposta

84

Se non si dispone di implementazioni concrete della classe e i metodi non sono static che cosa è il punto di testarli? Se hai una classe concreta, testerai questi metodi come parte dell'API pubblica della classe concreta.

So cosa stai pensando "Non voglio testare questi metodi più e più volte il motivo per cui ho creato la classe astratta", ma il mio contro-argomento è che il punto dei test unitari è quello di consentire agli sviluppatori per apportare modifiche, eseguire i test e analizzare i risultati. Una parte di queste modifiche potrebbe includere l'override dei metodi della classe astratta, sia protected che public, che potrebbe comportare modifiche comportamentali fondamentali. A seconda della natura di tali modifiche, potrebbe influire sul modo in cui l'applicazione viene eseguita in modi imprevisti, probabilmente negativi. Se si dispone di una buona unit test, i problemi derivanti da questi tipi di modifiche dovrebbero essere evidenti al momento dello sviluppo.

+0

Hai assolutamente ragione. Non ci ho pensato in questo modo. Ho solo pensato che fosse bello fare una prova per ogni classe del progetto, quindi posso essere sicuro che ogni classe funzioni come suppongo. – vasco

+10

La copertura del 100% del codice è un mito. Dovresti fare test sufficientemente accurati per coprire tutte le tue ipotesi conosciute su come dovrebbe comportarsi la tua applicazione (preferibilmente scritta prima di scrivere il codice per lo sviluppo di Test Driven). Attualmente sto lavorando a un team TDD che funziona molto bene e abbiamo solo una copertura del 63% a partire dal nostro ultimo build, tutto scritto come sviluppato. Quello è buono? chissà?, ma lo considererei una perdita di tempo per tornare indietro e cercare di aumentare quello più in alto. – nsfyn55

+0

Eviterei la duplicazione del test e dichiarerei i metodi come finali dove si applica. Il rovescio della medaglia di questo argomento è che dovrai cambiare più test se decidi di rifattorizzare o cambiare la funzione della classe astratta – HandyManDan

22

Creare una classe concreta che eredita la classe astratta e quindi verificare le funzioni della classe concreta eredita dalla classe astratta.

2

Uso beffardo. Controlla Mockito per esempio.

0

Non è possibile testare l'intera classe astratta. In questo caso hai metodi astratti, questo significa che dovrebbero essere implementati per classe che estende la classe astratta data.

In quel programmatore di classe è necessario scrivere il codice sorgente dedicato alla sua logica.

In altre parole, non c'è alcun senso di testare la classe astratta perché non si è in grado di verificarne il comportamento finale.

Se si dispone di funzionalità importanti non correlate a metodi astratti in alcune classi astratte, è sufficiente creare un'altra classe in cui il metodo astratto genererà alcune eccezioni.

10

Con la classe di esempio che hai pubblicato non sembra avere molto senso testare getFuel() e getSpeed() poiché possono solo restituire 0 (non ci sono setter).

Tuttavia, supponendo che questo sia solo un esempio semplificato a scopo illustrativo e che si abbiano motivi validi per testare i metodi nella classe base astratta (altri hanno già evidenziato le implicazioni), è possibile impostare il codice di test in modo che crea una sottoclasse anonima della classe base che fornisce solo implementazioni fittizie (non opzionali) per i metodi astratti.

Per esempio, nella vostra TestCase si potrebbe fare questo:

c = new Car() { 
     void drive() { }; 
    }; 

Poi testare il resto dei metodi, ad esempio:.

public class CarTest extends TestCase 
{ 
    private Car c; 

    public void setUp() 
    { 
     c = new Car() { 
      void drive() { }; 
     }; 
    } 

    public void testGetFuel() 
    { 
     assertEquals(c.getFuel(), 0); 
    } 

    [...] 
} 

(Questo esempio è basato sulla sintassi JUnit3 Per JUnit4 , il codice sarebbe leggermente diverso, ma l'idea è la stessa.)

+0

Grazie per la risposta. Sì, il mio esempio è stato semplificato (e non così buono). Dopo aver letto tutte le risposte qui, ho scritto una lezione fittizia. Ma come ha scritto @ nsfyn55 nella sua risposta, scrivo un test per ogni discendente di questa classe astratta. – vasco

1

Vorrei creare una sottoclasse jUnit che eredita dalla classe astratta. Questo può essere istanziato e avere accesso a tutti i metodi definiti nella classe astratta.

+3

Questo è un eccellente consiglio. Potrebbe essere migliorato fornendo un esempio, però. Forse un esempio della classe che stai descrivendo. – SDJMcHattie

3

Si potrebbe fare qualcosa di simile

public abstract MyAbstractClass { 

    @Autowire 
    private MyMock myMock;   

    protected String sayHello() { 
      return myMock.getHello() + ", " + getName(); 
    } 

    public abstract String getName(); 
} 

// this is your JUnit test 
public class MyAbstractClassTest extends MyAbstractClass { 

    @Mock 
    private MyMock myMock; 

    @InjectMocks 
    private MyAbstractClass thiz = this; 

    private String myName = null; 

    @Override 
    public String getName() { 
     return myName; 
    } 

    @Test 
    public void testSayHello() { 
     myName = "Johnny" 
     when(myMock.getHello()).thenReturn("Hello"); 
     String result = sayHello(); 
     assertEquals("Hello, Johnny", result); 
    } 
} 
5

Se avete bisogno di una soluzione in ogni caso (ad esempio, perché avete troppi implementazioni della classe astratta e la sperimentazione sarebbe sempre ripetere le stesse procedure) allora si potrebbe creare un classe di test astratta con un metodo di fabbrica astratto che sarà implementato dall'implementazione di quella classe di test. Questo esempio funziona o me con TestNG:

La classe di test astratta di Car:

abstract class CarTest { 

// the factory method 
abstract Car createCar(int speed, int fuel); 

// all test methods need to make use of the factory method to create the instance of a car 
@Test 
public void testGetSpeed() { 
    Car car = createCar(33, 44); 
    assertEquals(car.getSpeed(), 33); 
    ... 

Attuazione Car

class ElectricCar extends Car { 

    private final int batteryCapacity; 

    public ElectricCar(int speed, int fuel, int batteryCapacity) { 
     super(speed, fuel); 
     this.batteryCapacity = batteryCapacity; 
    } 

    ... 

classe di test Unità ElectricCarTest della Classe ElectricCar:

class ElectricCarTest extends CarTest { 

    // implementation of the abstract factory method 
    Car createCar(int speed, int fuel) { 
     return new ElectricCar(speed, fuel, 0); 
    } 

    // here you cann add specific test methods 
    ... 
0

È possibile creare un'istanza di una classe anonima e quindi testarla.

public class ClassUnderTest_Test { 

    private ClassUnderTest classUnderTest; 

    private MyDependencyService myDependencyService; 

    @Before 
    public void setUp() throws Exception { 
     this.myDependencyService = new MyDependencyService(); 
     this.classUnderTest = getInstance(); 
    } 

    private ClassUnderTest getInstance() { 
     return new ClassUnderTest() {  
      private ClassUnderTest init(
        MyDependencyService myDependencyService 
      ) { 
       this.myDependencyService = myDependencyService; 
       return this; 
      } 

      @Override 
      protected void myMethodToTest() { 
       return super.myMethodToTest(); 
      } 
     }.init(myDependencyService); 
    } 
} 

Tenete a mente che la visibilità deve essere protected per la proprietà myDependencyService della classe astratta ClassUnderTest.

È inoltre possibile combinare questo approccio in modo ordinato con Mockito. Vedi here.

Problemi correlati