che tipo di funzioni e / o classi sono impossibili da testare e perché


21

La principale scusa dello sviluppatore per non avere un buon test unitario è "Il codice non è progettato in modo unitamente testabile". Sto cercando di capire quale tipo di design e codice che non possono essere testati dall'unità.


2
La tua scusa? Un collega? Manager? Con quale lingua / framework stai lavorando?

1
Un sacco di codice legacy nell'applicazione e nessun tempo per la riprogettazione.
annodare il

4
@gnat: non sono d'accordo. La domanda che hai citato riguarda le situazioni in cui i test unitari non sono utili. La domanda attuale riguarda le situazioni che rendono difficili i test unitari.
Arseni Mourzenko,

@MainMa apparentemente leggiamo domande diverse. "Sto cercando di capire quale tipo di design e codice che non possono essere testati dall'unità." => "Quando è appropriato non test unitario?"
moscerino del

1
@manizzzz: potresti volerlo modificare nella domanda.
jmoreno,

Risposte:


27

Diversi fattori possono rendere il codice difficile da testare. In questo caso, il refactoring aiuta a migliorare il codice affinché sia ​​testabile.

Alcuni esempi di codice che probabilmente sarebbero difficili da testare:

  • Una funzione 1000-LOC,
  • Codice che si basa fortemente sullo stato globale,
  • Codice che richiede concreti e complicati per costruire oggetti, come il contesto del database, invece di fare affidamento su interfacce e Iniezione delle dipendenze,
  • Codice che si comporta lentamente ,
  • Codice spaghetti,
  • Codice legacy che è stato modificato per anni senza preoccuparsi della leggibilità o della manutenibilità,
  • Difficile capire il codice che non ha commenti o suggerimenti sull'intenzione originale dell'autore (ad esempio codice che utilizza nomi di variabili come function pGetDp_U(int i, int i2, string sText).

Si noti che la mancanza di un'architettura chiara non rende il codice difficile da testare, poiché i test unitari riguardano piccole parti del codice. Un'architettura poco chiara avrebbe comunque un impatto negativo sull'integrazione e sui test di sistema.


8
Inoltre è difficile testare il codice che non inietta dipendenze da funzioni non pure, come numeri casuali, ora corrente, I / O cablati, ecc.
9000,

è banale testare il codice in questo modo - hai solo bisogno del giusto strumento di test, non di manipolare il tuo codice per adattarlo a loro. Prova Microsoft Fakes per un esempio.
gbjbaanb,

@MainMa, mi piace questa risposta. Saresti anche disposto a commentare un po 'quali fattori spingono i diversi test verso l'integrazione e i test di sistema? So che la ragione per cui ho posto domande simili a quella qui in passato è che non avevo una tabella di marcia che spiegasse quali tipi di test sono meglio posizionati dove (o forse, più economicamente messo dove) - ho pensato i test unitari erano il solo ed unico.
J Trana,

14

Ci sono molte cose che rendono il codice difficile da testare. Per coincidenza, molti di questi hanno anche reso difficile mantenere il codice:

  • Legge delle violazioni di Demetra .
  • Creazione di oggetti all'interno di un metodo invece di iniettare dipendenze .
  • Accoppiamento stretto.
  • Scarsa coesione.
  • Si basa fortemente sugli effetti collaterali.
  • Si affida fortemente a globi o singoli.
  • Non espone molti risultati intermedi. (Una volta ho dovuto testare un'unità di una funzione matematica lunga dieci pagine con un singolo output e nessun risultato intermedio disponibile. I miei predecessori sostanzialmente hanno codificato qualunque risposta il codice fosse in grado di fornire).
  • Dipende fortemente e direttamente dai servizi che sono difficili da deridere, come i database.
  • L'ambiente di runtime è significativamente diverso dall'ambiente di sviluppo, come un target incorporato.
  • Unità disponibili solo in forma compilata (come una DLL di terze parti).

Penso che questa sia una risposta eccellente. Si toccano molti problemi a livello di codice e problemi di stato globale. @MainMa ha alcuni altri problemi che ritengo validi, ma meno definiti. Jeffery Thomas menziona I / O e UI. Penso che se aggiungi le parti positive di queste tre risposte avresti una risposta coerente. Mi piace molto questa risposta, anche se mi concentro sugli antipattern del codice.
M2tM,

1
Argh - niente di peggio di quanto afferma il test unitario che non ha alcuna somiglianza con i requisiti aziendali e sono solo l'output in un dato momento - simulazioni che sono configurate per essere chiamate 3 volte per esempio? Perché 3? Perché erano le 3 la prima volta che il test è stato eseguito / rant :)
Michael,

L'accoppiamento stretto è negativo solo quando è inappropriato. Uno stretto accoppiamento nel codice che sia altamente coesivo è una necessità. Ad esempio una dichiarazione variabile seguita dal suo utilizzo. Strettamente accoppiato, altamente coesivo.
dietbuddha,

1
I test unitari in cui l'output viene verificato rispetto a ciò che ha fatto il codice, senza alcun caso / giustificazione aziendale, sono chiamati Test di caratterizzazione. Sono utilizzati nella manutenzione in cui in precedenza non c'erano test e spesso non sono stati documentati requisiti e devi inserire qualcosa che si romperà se l'output di quella funzione cambia. Sono meglio di niente.
Andy Krouwel,

5

Esempi comuni di persone che non desiderano testare il codice:

  • Codice che interagisce direttamente con l'I / O (lettura di file, chiamate di rete dirette, ...).
  • Codice che aggiorna direttamente l'interfaccia utente.
  • Codice che fa riferimento direttamente a singoli o oggetti globali.
  • Codice che modifica implicitamente l'oggetto o lo stato dell'oggetto secondario.

Usando un framework simulato, tutti questi esempi possono essere testati in unità. È solo un lavoro per impostare le finte sostituzioni per le dipendenze interne.

Cose che non possono essere testate in unità:

  • Loop infiniti (per un gestore thread, un driver o qualche altro tipo di codice a esecuzione prolungata)
  • Alcuni tipi di operazioni di assemblaggio diretto (supportate da alcune lingue)
  • Codice che richiede un accesso privilegiato (non impossibile, ma non una buona idea)

2

Sono alcune aree che possono rendere più difficile la scrittura di unit test. Tuttavia, vorrei sottolineare che ciò non significa che dovresti scartare le tecniche utili semplicemente perché potrebbero aggiungere una certa complessità ai tuoi test. Come con qualsiasi codifica , dovresti fare la tua analisi per determinare se i benefici superano i costi e non accettare ciecamente ciò che alcuni tipi casuali pubblicano sulla rete.

Scritto male del codice progettato

  • accoppiamento inappropriato (di solito accoppiamento stretto dove non dovrebbe essere)
  • codice del lavello della cucina (dove una funzione ha troppe logica / responsabilità)

Affidamento dello stato in un ambito diverso

Il costo per la maggior parte di questi è fuori controllo a meno che tu non sappia cosa stai facendo. Sfortunatamente, molti spesso non sanno come usare queste tecniche in modo da mitigare cose come testare la complessità.

  • Singletons
  • globals
  • chiusure

Stato esterno / di sistema

  • Dipendenze hardware / dispositivo
  • Dipendenze di rete
  • Dipendenze del filesystem
  • Dipendenze tra processi
  • Altre dipendenze di chiamata di sistema

Concorrenza

  • Threading (blocchi, sezioni critiche, ecc.)
  • fork'ing
  • coroutine
  • Richiami
  • Signal Handlers (non tutti, ma alcuni)

2

Non esiste un codice che non possa essere testato. Ci sono, tuttavia, alcuni esempi di codice che è DAVVERO, DAVVERO difficile da testare (al punto da non valere la pena):

Interazioni hardware - Se il codice manipola direttamente l'hardware (ad esempio, scrivendo su un registro per spostare un dispositivo fisico), il test dell'unità potrebbe essere troppo difficile o costoso. Se si utilizza hardware reale per il test, può essere costoso ottenere un feedback adeguato sull'imbracatura di test (ancora più apparecchiature!) E, in caso contrario, è necessario emulare il comportamento esatto degli oggetti fisici - non è un piccolo trucco in alcuni casi.

Interazioni dell'orologio - Di solito è più facile, perché è quasi sempre possibile deridere le funzioni dell'orologio di sistema in modo piuttosto banale. Ma quando non puoi, allora questi test diventano ingestibili - i test basati sul tempo reale tendono a richiedere molto tempo per essere eseguiti e, nella mia esperienza, tendono ad essere molto fragili poiché i carichi di sistema fanno sì che le cose richiedano più tempo di quanto dovrebbero , causando errori di test fantasma.


0

I miei tre gruppi principali per questo sono:

  • codice che si basa su servizi esterni

  • sistemi che non consentono ai tester di modificare lo stato indipendentemente dall'applicazione.

  • ambienti di test che non replicano la configurazione di produzione.

Questo è ciò che ho sperimentato di più quando uno sviluppatore è diventato ingegnere addetto al controllo qualità.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.