2010-02-17 18 views
7

Ho questo metodo che dipende dalla data corrente. Controlla se oggi è Sun, Mon, Tue o Wed, quindi fornisce 5 giorni di anticipo per l'arrivo degli articoli spediti. Se è Thur, Fri o Sat, allora fornisce 6 giorni di tempo di consegna per tenere conto del fine settimana.Come testare la logica che dipende dalla data corrente

private DateTime GetEstimatedArrivalDate() 
{ 
    DateTime estimatedDate; 
    if (DateTime.Now.DayOfWeek >= DayOfWeek.Thursday) 
    { 
     estimatedDate = DateTime.Now.Date.AddDays(6); 
    } 
    else 
    { 
     estimatedDate = DateTime.Now.Date.AddDays(5); 
    } 
    return estimatedDate; 
} 

La logica di stima effettiva è più complessa. L'ho semplificato per lo scopo di questa domanda. La mia domanda è: come scrivo un unit test per qualcosa di simile che dipende dalla data odierna?

+0

La risposta è (quasi) nella questione "come faccio a scrivere un test unitario per qualcosa del genere che dipende dalla data odierna?" Rifatta il metodo per utilizzare l'iniezione delle dipendenze come nella risposta di Mark. –

risposta

17

è necessario passare la data corrente come parametro:

private DateTime GetEstimatedArrivalDate(DateTime currentDate) 
{ 
    DateTime estimatedDate; 
    if (currentDate.DayOfWeek >= DayOfWeek.Thursday) 
    { 
     estimatedDate = currentDate.AddDays(6); 
    } 
    else 
    { 
     estimatedDate = currentDate.AddDays(5); 
    } 
    return estimatedDate; 
} 

nel codice reale si chiama in questo modo:

DateTime estimatedDate = GetEstimatedArrivalDate(DateTime.Now.Date); 

Quindi è possibile testarlo nel modo seguente:

DateTime actual = GetEstimatedArrivalDate(new DateTime(2010, 2, 10)); 
DateTime expected = ...; 
// etc... 

Si noti che questo risolve anche un potenziale bug nel programma in cui la data cambia tra chiamate consecutive a DateTime.Now.

+0

Ho appena pubblicato la stessa risposta. Mi hai battuto alla velocità :) – Romain

+0

risolto il caso per te. –

+1

+1 per l'ultima riga ... –

0

Sembra che ci sia un numero limitato di casi che è possibile testarli in modo esplicito. Il metodo dipende dalla data odierna, ma l'output dipende solo dal giorno della settimana e ogni data ha un giorno della settimana.

0

È possibile passare un delegato che restituisce DateTime.Ora durante l'esecuzione normale, quindi nel test passa un altro delegato che restituisce una data fissa e asserisce il risultato in base a ciò.

0

Un modo "comune" di farlo è "falsificare" la data corrente del sistema (che può essere eseguita in diversi modi) e quindi testare il codice in date "conosciute".

Un altro modo interessante è quello di cambiare l'implementazione leggermente a:

private DateTime GetEstimatedArrivalDate() 
{ 
    return GetEstimatedArrivalDate(DateTime.Now); 
} 

private DateTime GetEstimatedArrivalDate(DateTime forDate) 
{ 
    DateTime estimatedDate; 
    if (forDate.DayOfWeek >= DayOfWeek.Thursday) 
    { 
     estimatedDate = forDate.Date.AddDays(6); 
    } 
    else 
    { 
     estimatedDate = forDate.Date.AddDays(5); 
    } 
    return estimatedDate; 
} 

E quindi utilizzare il metodo con un parametro per testare in date "immediate".

1

Darò la risposta controverso, non testarlo.

La logica è banale e ha zero dipendenze, credo in una buona copertura del codice ma non quando aumenta la complessità senza alcun guadagno reale.

+1

Sembra ragionevole, ma questo è esattamente il tipo di metodo che mi aspetto di cambiare in modo imprevisto. Se l'azienda cambia le proprie politiche di spedizione, inizia a offrire servizi a più livelli o cambia operatore. Ora, tecnicamente, potresti iniziare a testarlo una volta che diventa complicato, ma io tendo a pensare che questo sia un cardine significativo nell'applicazione, e dovrebbe essere sotto esame in modo che tutti sappiano nel momento in cui cambia. Nel caso in cui. – jcdyer

+0

Bah. Cosa succede se decidiamo di scambiare le posizioni di giovedì e venerdì durante la settimana? Non avresti mai * saputo che questo codice era rotto * fino a quando * era troppo tardi! * E se decidiamo di iniziare a contare i giorni * indietro *, beh, allora, non so nemmeno * cosa * dirti se puoi mi mostra una luce rossa lampeggiante "test fallito". ;) – Aaronaught

+0

Sono d'accordo con altri commentatori. La logica di questo metodo è in realtà non trival. L'ho reso semplice solo allo scopo di mostrarlo come un esempio per la mia domanda. Voglio testarlo a fondo. –

9

in generale, che ci si vuole astratta metodo di ottenimento la data e l'ora correnti dietro un'interfaccia, ad esempio:

public interface IDateTimeProvider 
{ 
    DateTime Now { get; } 
} 

Il servizio vero sarebbe:

public class DateTimeProvider: IDateTimeProvider 
{ 
    public DateTime Now 
    { 
     get 
     { 
      return DateTime.Now; 
     } 
    } 
} 

E un il servizio di prova sarebbe:

public class TestDateTimeProvider: IDateTimeProvider 
{ 
    private DateTime timeToProvide; 
    public TestDateTimeProvider(DateTime timeToProvide) 
    { 
     this.timeToProvide = timeToProvide; 
    } 

    public DateTime Now 
    { 
     get 
     { 
      return timeToProvide; 
     } 
    } 
} 

Per i servizi che richiedono l'ora corrente, farli prendere un IDa teTimeProvider come dipendenza. Per il vero, passa un nuovo DateTimeProvider(); quando sei un componente, passa un nuovo TestDateTimeProvider (timeToTestFor).

+1

Darò questo a + 1 perché è assolutamente giusto, ma allo stesso tempo ritengo che sia un po 'esagerato solo per testare un metodo trival. Ammiro la tua dedizione alla copertura, e probabilmente lo adotterei se ci fossero molti metodi o qualche metodo complesso che necessitava di test :) –

+0

Potrebbe essere banale ora, ma le cose possono facilmente cambiare. Ad ogni modo, +1 come farei sicuramente un adattatore attorno alle classi DateTime per abilitare i test. Inoltre, per un TDDer, questa sarebbe la via normale. – Finglas

+0

In una nota a margine, non sarebbe meglio utilizzare una struttura di simulazione, piuttosto che creare un "servizio di prova" che ora è necessario mantenere anche tu? Sembra un po 'noioso? –

2

Fai la tua classe prendere un parametro IClock (via costruttore o proprietà)

interface IClock 
{ 
    DateTime Now { get; } 
} 

È quindi possibile utilizzare un'implementazione falso per testare

class FakeClock : IClock 
{ 
    DateTime Now { get; set } 
} 

e una reale attuazione il resto del tempo.

class SystemClock : IClock 
{ 
    DateTime Now { get { return DateTime.Now; } } 
} 
0

Vorrei suggerire di fare questo come Mark suggests, ma con l'aggiunta di una chiamata di overload per l'uso in produzione che richiede alcun parametro e usa DateTime.Now

private DateTime GetEstimatedArrivalDate() 
{ 
    return GetEstimatedArrivalDate(DateTime.Now); 
} 

private DateTime GetEstimatedArrivalDate(DateTime currentDate) 
{ 
    DateTime estimatedDate; 
    if (currentDate.DayOfWeek >= DayOfWeek.Thursday) 
    { 
     estimatedDate = currentDate.AddDays(6); 
    } 
    else 
    { 
     estimatedDate = currentDate.AddDays(5); 
    } 
    return estimatedDate; 
} 
Problemi correlati