2011-02-05 33 views
25

Capisco l'idea dietro lo sviluppo basato sui test, scrivo prima i test, il codice contro i test fino al successo. Non è ancora venuto insieme per me nel mio flusso di lavoro.Esempi di test unitari?

Potete darmi alcuni esempi in cui i test unitari potrebbero essere utilizzati in un contesto di sviluppo web front o back-end?

+2

Mi raccomando questa risposta: http://stackoverflow.com/questions/18601/best-practice-for-integrating-tdd-with-web-application-development – Herter

+0

Come ci sono infiniti esempi, questa domanda è troppo ampio – Raedwald

risposta

64

Non hai specificato una lingua, quindi cercherò di mantenerla in qualche modo generica. Sarà difficile però, dal momento che è molto più semplice esprimere concetti con codice reale.

Le prove di unità possono essere un po 'di confusione all'inizio. A volte non è sempre chiaro come testare qualcosa o quale sia lo scopo di un test.

Mi piace trattare il test dell'unità come un modo per testare piccoli pezzi di codice.

Il primo posto in cui utilizzo i test di unità è verificare che alcuni metodi funzionino come mi aspetto in tutti i casi. Ho appena scritto un metodo di convalida per un numero di telefono per il mio sito. Accetto qualsiasi input, da 123-555-1212, (123) 555-1212, ecc. Voglio essere sicuro che il mio metodo di validazione funzioni per tutti i possibili formati. Senza un test unitario, sarei obbligato a inserire manualmente ciascun formato diverso e verificare che il modulo sia inserito correttamente. Questo è molto noioso e soggetto a errori. Più tardi, se qualcuno apporta una modifica al codice di convalida del telefono, sarebbe bello se potessimo controllare facilmente per assicurarci che non si rompesse niente altro. (Forse abbiamo aggiunto il supporto per il codice del paese). Quindi, ecco un esempio banale:

public class PhoneValidator 
{ 
    public bool IsValid(string phone) 
    { 
      return UseSomeRegExToTestPhone(phone); 
    } 
} 

ho potuto scrivere uno unit test come questo:

public void TestPhoneValidator() 
{ 
    string goodPhone = "(123) 555-1212"; 
    string badPhone = "555 12" 

    PhoneValidator validator = new PhoneValidator(); 

    Assert.IsTrue(validator.IsValid(goodPhone)); 
    Assert.IsFalse(validator.IsValid(badPhone)); 
} 

Quei 2 linee Assert sarà verificare che il valore restituito da IsValid() è vero e falso, rispettivamente.

Nel mondo reale, probabilmente ci sono molti e molti esempi di numeri di telefono buoni e cattivi. Ho circa 30 numeri di telefono su cui faccio i test. È sufficiente eseguire questo test dell'unità in futuro per sapere se la logica di convalida del telefono è interrotta.

Possiamo anche utilizzare i test di unità per simulare cose al di fuori del nostro controllo.

I test di unità devono essere eseguiti indipendentemente dalle risorse esterne. I test non devono dipendere dalla presenza di un database o dalla disponibilità di un servizio web. Quindi, simuliamo queste risorse, quindi possiamo controllare ciò che restituiscono. Ad esempio, nella mia app, non posso simulare una carta di credito rifiutata al momento della registrazione. Probabilmente la banca non vorrebbe che io inviassi migliaia di carte di credito sbagliate solo per assicurarmi che il mio codice di gestione degli errori fosse corretto. Ecco alcuni esempi di codice:

public class AccountServices 
{ 
    private IBankWebService _webService = new BankWebService(); 

    public string RegisterUser(string username, string creditCard) 
    { 
     AddUserToDatabase(username); 

     bool success = _webService.BillUser(creditCard); 

     if (success == false) 
      return "Your credit card was declined" 
     else 
      return "Success!" 

    } 
} 

Questo è dove il test dell'unità è molto confuso e non ovvio. Cosa dovrebbe fare un test di questo metodo? La prima cosa, sarebbe molto bello se potessimo verificare che, se la fatturazione falliva, veniva restituito il messaggio di errore appropriato. Come si scopre, usando una finta, c'è un modo. Usiamo ciò che viene chiamato Inversion of Control. Al momento, AccountServices() è responsabile della creazione dell'oggetto BankWebService.Lasciamo che il chiamante di questa classe fornirle però:

public class AccountServices 
{ 
    public AccountServices(IBankWebService webService) 
    { 
     _webService = webService; 
    } 

    private IBankWebService _webService; 

    public string RegisterUser(string username, string creditCard) 
    { 
     AddUserToDatabase(username); 

     bool success = _webService.BillUser(creditCard); 

     if (success == false) 
      return "Your credit card was declined" 
     else 
      return "Success!" 

    } 
} 

perché il chiamante è responsabile della creazione dell'oggetto BankWebService, la nostra prova di unità in grado di creare un falso uno:

public class FakeBankWebService : IBankWebService 
{ 
    public bool BillUser(string creditCard) 
    { 
     return false; // our fake object always says billing failed 
    } 
} 

public void TestUserIsRemoved() 
{ 
    IBankWebService fakeBank = FakeBankWebService(); 

    AccountServices services = new AccountServices(fakeBank); 

    string registrationResult = services.RegisterUser("test_username"); 

    Assert.AreEqual("Your credit card was declined", registrationResult); 
} 

Utilizzando tale oggetto falso , ogni volta che viene chiamato BillUser() della nostra banca, il nostro oggetto falso restituirà sempre false. Il nostro test unitario ora verifica che se la chiamata alla banca fallisce, RegisterUser() restituirà il messaggio di errore corretto.

Supponiamo che un giorno si stanno facendo alcuni cambiamenti, e un bug insinua in: "Successo"

public string RegisterUser(string username, string creditCard) 
{ 
    AddUserToDatabase(username); 

    bool success = _webService.BillUser(creditCard); 

    if (success) // IT'S BACKWARDS NOW 
     return "Your credit card was declined" 
    else 
     return "Success!" 

} 

Ora, quando la fatturazione non riesce, il metodo Your RegisterUser() restituisce. Fortunatamente, hai un test unitario scritto. Quel test unitario ora fallirà perché non sta più restituendo "La tua carta di credito è stata rifiutata".

È molto più facile e veloce trovare il bug in questo modo che compilare manualmente il modulo di registrazione con una carta di credito scadente, solo per controllare il messaggio di errore.

Una volta esaminate le diverse strutture di derisione, ci sono cose ancora più potenti che è possibile fare. È possibile verificare i metodi falsi sono stati chiamati, è possibile verificare il numero di volte che è stato chiamato un metodo, è possibile verificare i parametri con cui sono stati chiamati i metodi, ecc.

Penso che una volta comprese queste 2 idee, capirai più che sufficiente per scrivere un sacco di unit test per il tuo progetto.

Se ci dici la lingua che stai utilizzando, possiamo indirizzarti meglio.

Spero che questo aiuti. Mi scuso se parte di esso è fonte di confusione. Lo pulirò se qualcosa non ha senso.

+2

Questo è super utile, volevo solo avere una comprensione di base, grazie mille! – eighteyes

+2

Felice che aiuti. Il test unitario sembra essere sorprendentemente difficile da capire all'inizio, e quindi sorprendentemente facile una volta scoccato. Per me, è stato più imparare come scrivere codice verificabile. Che lingua stai usando? Possiamo dare consigli migliori per la tua lingua particolare. – mfanto

+0

Javascript, PHP e Python principalmente. – eighteyes