2012-02-23 8 views
8

Ho un metodo che implementa una logica diversa sui dati recuperati da un DB a seconda di quale sia la data corrente.Logica basata sul tempo di test delle unità in Java

Voglio testarlo facendo in modo che l'unit test crei oggetti, li salvi nel DB e invochino il metodo testato. Tuttavia, al fine di avere risultati prevedibili ho bisogno di cambiare la data di sistema ogni volta e non so come farlo in Java.

Suggerimenti?

+2

cambia il tuo metodo in modo da dare effettivamente il tempo come parametro, quindi puoi testarlo dando ogni volta che vuoi. Cosa parla di questo? – belgther

+0

Puoi dire come stai ricevendo la data e l'ora correnti? Forse il mocking funzionerebbe ... –

+0

Per ora solo con 'new Date()', forse ho bisogno di una classe TimeProvider – Alex

risposta

14

È possibile generare i risultati previsti utilizzando la data corrente.

Oppure scrivi il tuo sistema per utilizzare una data/ora che gli dai durante il test (piuttosto che l'orologio) In questo modo il tempo è sempre quello che il test si aspetta.

uso qualcosa come

interface TimeSource { 
    long currentTimeMS(); // actually I have currentTimeNS 
    void currentTimeMS(long currentTimeMS); 
} 

enum VanillaTimeSource implements TimeSource { 
    INSTANCE; 

    @Override 
    public long currentTimeMS() { 
     return System.currentTimeMillis(); 
    } 

    @Override 
    public void currentTimeMS(long currentTimeMS) { 
     // ignored 
    } 
} 

class FixedTimeSource implements TimeSource { 
    private long currentTimeMS; 
    @Override 
    public long currentTimeMS() { 
     return currentTimeMS; 
    } 

    @Override 
    public void currentTimeMS(long currentTimeMS) { 
     this.currentTimeMS =    currentTimeMS; 
    } 
} 

Nei test utilizzo un FixedTimeSource che possono essere azionati per esempio i dati impostato da input/eventi. In produzione utilizzo una VanillaTimeSource.INSTANCE che ignora i tempi in input/eventi e utilizza l'ora corrente.

+0

Primo suggerimento probabilmente vinto lavorare Avrei anche accennato al fatto che i dati vengono recuperati esattamente anche in base alla data (secondo una determinata colonna). Ciò significa che anche se la logica non è diversa, dovrei generare risultati diversi in base a quasi tutti i giorni dell'anno.Probabilmente andrò per il secondo suggerimento – Alex

+0

È possibile generare il risultato previsto come parte dell'esecuzione del test, prima di controllare il risultato. Non è necessario generarli in anticipo. –

+0

Effettuare una classe/componente che riceve il tempo come parametro assicura che sia un buon componente perché si disaccoppia dal tempo di sistema. Metti tutte le configurazioni specifiche dell'applicazione ("usando l'ora del sistema") all'esterno del componente. Deve solo dipendere da stimoli configurabili esterni. – helios

8

È necessario cercare di iniettare qualcosa nella classe che consente di personalizzare il modo in cui viene presentato il tempo.

Per esempio

public interface TimeProvider { 
    DateTime getCurrentTime(); 
} 

public class UnderTest { 

    // Inject this in some way (e.g. provide it in the constructor) 
    private TimeProvider timeProvider; 

    public void MyMethod() { 
    if (timeProvider.getCurrentTime() == "1234") { 
     // Do something 
    } 
    } 
} 

Ora nel tuo test di unità è possibile fornire un falso implementazione di fornitore di tempo. Nel codice di produzione reale è possibile solo restituire l'ora corrente.

+0

Ovviamente, potresti anche fornirlo come parametro come suggerisce 'belgther' :) –

2

Ho avuto un problema simile di recente con il codice non ho potuto refactoring troppo (limiti di tempo, non volevo inavvertitamente rompere nulla). Aveva un metodo che volevo testare, chiamato System.currentTimeMillis() e il caso che volevo testare dipendeva da cosa restituiva quel valore. Qualcosa di simile:

public class ClassINeedToTest { 
    public boolean doStuff() { 
     long l = System.currentTimeMillis(); 
     // do some calculation based on l 
     // and return the calculation 
    } 
} 

Per consentire unit test, ho refactoring la classe in modo che aveva un metodo di supporto che è stato protetto

protected long getCurrentTimeMillis() { 
    // only for unit-testing purposes 
    return System.currentTimeMillis(); 
} 

e questo metodo è stato chiamato da doStuff(). Questo non ha cambiato la funzionalità, ma ora ha fatto sì che, quando lo chiamo io nell'unità di test, ho potuto quindi sovrascrivere questo per restituire un valore specifico, come

ClassINeedToTest testClass = new ClassINeedToTest() { 
    protected long getCurrentTimeMillis() { 
     // return specific date for my test 
     return 12456778L; 
    } 
}; 
boolean result = testClass.doStuff(); 
// test result with an assert here 

Questo significa tuttavia che ho inquinato la interfaccia della mia classe, quindi potresti decidere che il costo è troppo alto. Ci sono probabilmente dei modi migliori se puoi rifattorizzare di più il codice.

Problemi correlati