What's the decisive advantage of unit testing vs integration testing?
Questa è una falsa dicotomia.
Test di unità e test di integrazione hanno due scopi simili ma diversi. Lo scopo del test unitario è assicurarsi che i tuoi metodi funzionino. In termini pratici, i test unitari assicurano che il codice soddisfi il contratto delineato dai test unitari. Questo è evidente nel modo in cui sono progettati i test unitari: specificano specificamente cosa il codice dovrebbe fare e asserire che il codice lo fa.
I test di integrazione sono diversi. I test di integrazione esercitano l'interazione tra i componenti software. È possibile avere componenti software che superano tutti i loro test e continuano a fallire i test di integrazione perché non interagiscono correttamente.
Tuttavia, se c'è un vantaggio decisivo per i test unitari, è questo: i test unitari sono molto più facili da configurare e richiedono molto meno tempo e impegno rispetto ai test di integrazione. Se usato correttamente, i test unitari incoraggiano lo sviluppo di codice "testabile", il che significa che il risultato finale sarà più affidabile, più facile da capire e più facile da mantenere. Il codice verificabile ha alcune caratteristiche, come un'API coerente, un comportamento ripetibile e restituisce risultati facili da affermare.
I test di integrazione sono più difficili e più costosi, perché spesso hai bisogno di una simulazione elaborata, di una configurazione complessa e di asserzioni difficili. Al più alto livello di integrazione del sistema, immagina di provare a simulare l'interazione umana in un'interfaccia utente. Interi sistemi software sono dedicati a quel tipo di automazione. Ed è l'automazione che cerchiamo; il test umano non è ripetibile e non ha le stesse dimensioni dei test automatizzati.
Infine, i test di integrazione non garantiscono la copertura del codice. Quante combinazioni di loop di codice, condizioni e rami stai testando con i tuoi test di integrazione? Lo sai davvero? Ci sono strumenti che puoi usare con unit test e metodi sotto test che ti diranno quanta copertura di codice hai e cosa è la complessità ciclomatica del tuo codice. Ma funzionano davvero bene solo a livello di metodo, dove i test unitari sono in diretta.
Se i tuoi test cambiano ogni volta che fai refactoring, questo è un altro problema. I test unitari dovrebbero riguardare la documentazione di ciò che fa il software, dimostrando che lo fa, e quindi dimostrare che lo fa di nuovo quando si refactoring l'implementazione sottostante. Se la tua API cambia o hai bisogno che i tuoi metodi cambino in base a un cambiamento nella progettazione del sistema, è ciò che dovrebbe accadere. Se sta accadendo molto, considera prima di scrivere i tuoi test, prima di scrivere il codice. Questo ti costringerà a pensare all'architettura generale e ti consentirà di scrivere codice con l'API già stabilita.
Se passi un sacco di tempo a scrivere test unitari per codice banale come
public string SomeProperty { get; set; }
allora dovresti riesaminare il tuo approccio. Il testing delle unità dovrebbe testare comportamento, e non c'è alcun comportamento nella riga di codice sopra. Tuttavia, hai creato una dipendenza dal tuo codice da qualche parte, dal momento che quella proprietà è quasi certamente indicata altrove nel tuo codice. Invece di farlo, prendi in considerazione la scrittura di metodi che accettano la proprietà necessaria come parametro:
public string SomeMethod(string someProperty);
Ora il tuo metodo non ha alcuna dipendenza da qualcosa al di fuori di se stesso, ed è ora più testabile, poiché è completamente autonomo. Certo, non sarai sempre in grado di farlo, ma sposta il tuo codice nella direzione di essere più testabile, e questa volta stai scrivendo un test unitario per comportamento effettivo.