Il test con tutte le dipendenze in atto è ancora importante, ma è più nel regno dei test di integrazione, come ha detto Jeffrey Faust.
Uno degli aspetti più importanti dei test unitari è quello di rendere i test affidabili. Se non ti fidi del fatto che un test di passaggio significhi veramente che le cose sono buone e che un test non funzionante significa davvero un problema nel codice di produzione, i tuoi test non sono così utili come possono essere.
Per rendere affidabili le tue prove, devi fare alcune cose, ma mi concentrerò su una sola per questa risposta. Devi assicurarti che siano facili da eseguire, in modo che tutti gli sviluppatori possano eseguirli facilmente prima di controllare il codice. "Facile da eseguire" significa che i tuoi test scorrono rapidamente e non c'è una configurazione estesa o setup necessario per farli andare. Idealmente, chiunque dovrebbe essere in grado di controllare l'ultima versione del codice, eseguire subito i test e vederli passare.
Eliminare le dipendenze da altre cose (file system, database, servizi Web, ecc.) consente di evitare la necessità di configurazione e rende voi e altri sviluppatori meno suscettibili a situazioni in cui siete tentati di dire "Oh, i test sono falliti perché Non ho la condivisione della rete impostata, beh, li eseguirò più tardi ".
Se vuoi testare ciò che fai con alcuni dati, il tuo test di unità per quel codice di logica aziendale non dovrebbe preoccuparsi di come ottieni quei dati. Essere in grado di testare la logica di base della tua applicazione senza dipendere da materiale di supporto come i database è fantastico. Se non lo fai, ti stai perdendo.
P.S. Devo aggiungere che è sicuramente possibile effettuare interventi di supervisione in nome della testabilità. Testare la tua applicazione ti aiuta a mitigarlo. Ma in ogni caso, le cattive implementazioni di un approccio non rendono l'approccio meno valido. Qualsiasi cosa può essere usata impropriamente e esagerata se non si continua a chiedere "perché sto facendo questo?" durante lo sviluppo.
Per quanto riguarda le dipendenze interne, le cose diventano un po 'fangose. Il modo in cui mi piace pensarlo è che voglio proteggere il più possibile la mia classe dal cambiare per ragioni sbagliate. Se ho un setup come qualcosa del genere ...
public class MyClass
{
private SomeClass someClass;
public MyClass()
{
someClass = new SomeClass();
}
// use someClass in some way
}
In genere non mi interessa come viene creato SomeClass
. Voglio solo usarlo. Se SomeClass cambia e ora richiede parametri per il costruttore ... non è un mio problema. Non dovrei cambiare MyClass per adattarlo.
Ora, questo è solo toccando la parte del design. Per quanto riguarda i test unitari, voglio anche proteggermi dalle altre classi. Se sto testando MyClass, mi piace sapere per certo che non ci sono dipendenze esterne, che SomeClass a un certo punto non ha introdotto una connessione al database o qualche altro collegamento esterno.
Ma un problema ancora più grande è che so anche che alcuni dei risultati dei miei metodi si basano sull'output di alcuni metodi su SomeClass. Senza il mocking / stubbing di SomeClass, potrei non avere modo di variare l'input della domanda. Se sono fortunato, potrei essere in grado di comporre il mio ambiente all'interno del test in modo tale da far scattare la giusta risposta da SomeClass, ma andare in questo modo introduce complessità nei miei test e renderli fragili.
Riscrivere MyClass per accettare un'istanza di SomeClass nella funzione di costruzione mi consente di creare un'istanza fittizia di SomeClass che restituisce il valore desiderato (tramite un framework di simulazione o con una simulazione manuale). In genere non ho per introdurre un'interfaccia in questo caso. Se farlo o no è per molti versi una scelta personale che può essere dettata dal linguaggio scelto (ad esempio, le interfacce sono più probabili in C #, ma sicuramente non ne avrà bisogno in Ruby).