Ha qualche logica nel codice sorgente per poter eseguire alcuni test come buona pratica?

2

La domanda non riguarda la modifica della visibilità del metodo / funzione o l'estrazione della variabile locale nella variabile di istanza per eseguire i test unitari.

Ci sono casi in cui possiamo includere alcune logiche nel codice sorgente per aiutarci a eseguire alcuni test funzionali o test end-to-end. In generale, questi codici interagiscono direttamente con la logica principale e se il codice di test è difettoso, la logica di business è influenzata.

Un esempio potrebbe essere che abbiamo un codice di prova attorno alla logica data / ora in modo che se i tester rendono diverso tempo server e client, questo codice potrebbe aiutarli a eseguire alcuni test.

Quindi, quanto pensi di averlo, se non c'è un altro modo facile (ish) per testare questa parte della logica aziendale?

Per favore commenta se questa semplice spiegazione è troppo vaga e hai bisogno di più esempi.

    
posta user2001850 06.08.2014 - 21:24
fonte

4 risposte

12

Sulla logica nei test

There are cases where we could include some logic in the source code in order to help us performing some functional tests or end-to-end tests

I test unitari sono un'area di ingegneria del software in cui le persone sembrano diventare molto dogmatiche. "Fallo in questo modo o non è un test unitario" - questo può interferire con l'esecuzione di test. Le persone hanno argomentato in ogni modo e continueremo a discuterne per un po 'di tempo.

Uno dei miei documenti preferiti sui test è The Way of Testivus - è una lettura buona (e divertente) .

Il fatto è scrivere il test che deve essere scritto . Se ciò significa inserire la logica, allora c'è logica in là.

Il pericolo di questo è chi mette alla prova la logica nei test ( Ipocrisia guidata dai test ? Chi prova il test? ). Il problema è che se la tua logica di test e la tua logica di business sono entrambe infrante, potresti perdere un vero test fallito.

Sul codice con logica di test

Un altro aspetto di questa domanda è il "abbiamo bloccato un codice nel build che va al QA (e presumibilmente alla produzione - perché non si cambia la build dopo averlo dato al QA ...) per fare il proprio lavoro più facile.

C'è un vero pericolo qui.

Quando inserisci il codice di test nella build, è possibile che ne venga dato il solletico in produzione. "Ma non succederà mai" è successo troppo spesso. Il codice ti consente di impostare il timestamp o entrare in un determinato stato che ti permette di impostare alcuni dati senza passare attraverso altri processi.

Queste sono cose cattive . Non farli.

One example could be that we have some test code around date/time logic so that if the testers make server and client time different, this code could help them to perform some tests.

Non farlo. E qui sono piuttosto dogmatico. C'è una differenza tra avere il codice di test unitario che non viene mai inserito in una build di produzione dove è importante eseguire il test ... e il codice che sta per essere prodotto.

Il codice di prova è lì dentro che ti permette di bypassare il normale flusso del codice che un utente dovrebbe fare ha diversi problemi con esso:

  • L'intero processo non è in fase di test. Il tester del controllo qualità sta saltando il codice in un modo che un utente normale non può. Ciò significa che il flusso di lavoro effettivo non viene testato. Hai bloccato i dati in qualche punto tramite un'interfaccia di amministrazione / test che inizializza la struttura prima di usarla ... ma il tuo codice normale no e hai un bug che il QA non troverà.
  • C'è un gancio che può essere utilizzato da qualcun altro. Sappiamo che tutti gli utenti sono gentili e che non vanno a curiosare in posti che non dovrebbero cercare. Certo, hai nascosto di dover tenere premuto lo shift-control-alt e 'cat' allo stesso tempo ... ma non hai mai avuto un gatto in realtà un passo sulla tastiera per farlo in un modo che solo un gatto può. E i gatti non sono i più nefandi degli utenti di tastiera: anche le persone con intenzioni meno ambiziose troveranno questo hook.

Se hai bisogno di un ambiente che rende più facile testare una cosa particolare, configura quell'ambiente. Vuoi che i tester abbiano la possibilità di fare qualche test con il client e il server fuori sincrono? Crea una VM che ricrea facilmente questo ambiente piuttosto che inserire un codice che faccia interpretare al client i suoi timestamp come 30 secondi nel passato (o nel futuro).

Non inserire il codice di test nella build di produzione. Assicurati di testare la build di produzione. Ciò potrebbe richiedere un processo di test più lungo, ma le alternative possono essere pessime (mancare un bug o lasciare che qualcuno usi il software in modo improprio).

    
risposta data 06.08.2014 - 22:03
fonte
7

Il codice che viene aggiunto, ad esempio, a una classe, a scopo di test, è sintomatico del codice che non è stato scritto per essere testabile.

Ti darò un esempio. In una delle mie attuali applicazioni, esiste un parser che legge un file di dati contenente coppie di chiavi / valori e crea una struttura di dati ricorsiva rappresentativa. Le specifiche sono qui , se sei interessato.

Per accelerare il processo di test, ho eseguito l'override del metodo ToString() nella classe Node (una struttura dati ricorsiva contenente il nodo dati corrente e un elenco di nodi discendenti) e inserisco del codice lì in modo ricorsivo cammina i nodi e costruisce una stringa contenente una rappresentazione leggibile dal pubblico del contenuto di quella parte dell'albero. Quindi analizzo un po 'del file di dati, analizzo l'output che ottengo da ToString() e, se sembra buono, incollo l'output in un test unitario e lo asserisco.

In questo modo è possibile creare una suite di test in modo relativamente rapido. Il problema, ovviamente, è che se cambi qualsiasi cosa, in particolare il metodo ToString() , interrompe tutti i tuoi test. Inoltre, poiché i test non testano funzionalità specifiche (preferendo lanciare una rete ampia invece, e sperando che venga raggiunta una copertura di codice adeguata), sono essenzialmente test "vai, non vai". Se il test fallisce, devi analizzare l'output per determinare il perché (un processo reso più facile con l'uso dei metodi diff).

Quindi il compromesso è che tali test sono relativamente facili da fabbricare e risparmiare un sacco di tempo, ma il lato negativo è che il livello di copertura del codice è incerto, i test sono fragili e un test fallito non ti dice nulla senza ulteriori analisi.

Un test brutto è meglio di nessun test

When the code is ugly, the tests may be ugly.
You don’t like to write ugly tests, but ugly code needs testing the most.
Don’t let ugly code stop you from writing tests, but let ugly code stop you from writing more of it.

- The Way of Testivus

    
risposta data 06.08.2014 - 22:05
fonte
1

La domanda sembra un po 'vaga, quindi cercherò di rispondere al meglio che posso. Se la risposta non aiuta a sentirsi libero di aggiungere ulteriori informazioni e posso aggiungere una modifica per mettere a fuoco le nuove informazioni.

In genere è una buona idea testare i dati passati in una funzione o un metodo. Uno dei test più popolari sarebbe un test nullo o nessuno. Questi tipi di test possono garantire che i dati trasmessi non interrompano la funzione / metodo. Inoltre, la funzione / metodo ha la possibilità di gestire o restituire un errore all'utente senza che si verifichi un'eccezione del puntatore nullo, o simile, generata causando l'arresto anomalo del programma.

Inoltre è una buona idea controllare anche altre cose. Se si suppone di ottenere un datetime ma solo ricevere una data questo potrebbe essere un problema. Potresti voler lanciare un'eccezione o semplicemente aggiungere un tempo predefinito in modo che il codice più avanti nella funzione / metodo non si interrompa.

Non vorrei fornire codice che preforma lo stesso tipo di test che un test unitario fornirebbe. Significa che non dovresti avere il codice che controlla se una funzione / metodo è in grado di gestire un determinato tipo di dati. Questo può essere estratto in un test eseguito in un ambiente non di produzione.

Sembra che tu stia cercando di aggiungere codice per controllare un parametro passato. Ad esempio, se una chiamata a un'API restituisce l'ora del server e la macchina client controlla se l'ora coincide o è vicina al proprio orario interno. Supponendo che lo stesso fuso orario o entrambe le macchine stiano confrontando utilizzando UTC (è utile avere una base comune da confrontare), si vorrebbe quasi certamente verificare che i dati sensibili al tempo abbiano un timestamp corretto nel codice. Da lì puoi far sapere all'utente che i loro dati potrebbero non essere aggiornati o che il loro orario di sistema è disattivato.

    
risposta data 06.08.2014 - 21:44
fonte
1

Il tuo problema richiede il Pattern IOC . Dove mai hai bisogno di un DateTime userai il DateTimeProvider che hai iniettato nella tua classe come parametro costruttore.

Ora, quando stai testando la tua classe, puoi usare il tuo finto / manichino / qualsiasi DateTimeProvider e fare quello che vuoi.

Ora le tue classi non conterranno alcun codice di test. Potrebbe sembrare un codice di piastra di caldaia aggiuntivo, ma in tal caso ti consiglio di utilizzare un contenitore IOC.

    
risposta data 07.08.2014 - 00:30
fonte

Leggi altre domande sui tag