Test: deterministico o non deterministico?


16

È meglio avere uno a

  • Suite di test deterministico, che si traduce nel successo degli stessi test
  • Suite di test non deterministico, che potenzialmente copre probabilmente più casi

?

Esempio: si scrive una suite di test per testare la funzionalità del controller in un'applicazione MVC. Il controller richiede i dati dell'applicazione da un database come input durante il test. Ci sono due opzioni per fare questo:

  • Si hardcode quali righe del database di test sono selezionate come input (ad es. La decima e la quarta riga)
  • Si utilizza un generatore di numeri casuali per selezionare in modo pseudocasuale i dati dal database (due righe selezionate da un generatore di numeri casuali)

Il primo è deterministico: ogni esecuzione del test per la stessa revisione del codice dovrebbe produrre lo stesso risultato. Il secondo non è deterministico: ogni esecuzione della suite di test ha la possibilità di produrre un risultato diverso. I dati raccolti in modo casuale potrebbero tuttavia essere una migliore rappresentazione dei casi limite di dati. Potrebbe simulare meglio un utente che fornisce ai nostri controller dati imprevedibili?

Quali sono i motivi per scegliere l'uno rispetto all'altro?


5
Quel test fallisce, a volte. martinfowler.com/articles/nonDeterminism.html

Grazie per quel link. Con questo articolo in mente, ho sentito il bisogno di chiarire che non determinismo significa nel contesto di questa suite di test. Poiché i dati vengono selezionati in modo casuale da un database, tutti i dati inviati al controller sono dati validi per impostazione predefinita. Ciò significa che non esistono falsi negativi nella suite di test quando si tratta di non determinismo. In un certo senso, questa casualità simula un utente che seleziona i dati "a caso" per l'uso in un controller. Questo non è necessariamente lo stesso non determinismo di cui l'articolo discute, giusto?
DCKing,


10
@DCKing: considera cosa succede se il test fallisce. Ok, hai un bug. Uh, e adesso? Eseguilo di nuovo in modalità debug! Dove ci riesce! Come fa le successive cento volte che lo esegui, e poi annulli il problema come un raggio cosmico. Non determinisim nei test sembra assolutamente impraticabile. Se senti la necessità di coprire più terreno nei tuoi casi di test, copri più terreno. Inizializza il tuo RNG con un seme impostato ed esegui il "test" alcune centinaia di volte con valori costantemente casuali.
Phoshi,

1
(finalmente sono arrivato a una macchina in cui ho potuto cercare correttamente Twitter - il " Quel test fallisce a volte " proviene da #FiveWordTechHorrors su Twitter - voleva accreditarlo correttamente)

Risposte:


30

Quando ogni esecuzione della suite di test ti dà la possibilità di produrre un risultato diverso, il test è quasi del tutto inutile: quando la suite ti mostra un bug, hai un'alta probabilità di non poterlo riprodurre e quando provi a risolvere il bug, non è possibile verificare se la correzione funziona.

Pertanto, quando ritieni di dover utilizzare un qualche tipo di generatore di numeri casuali per generare i tuoi dati di test, accertati di inizializzare sempre il generatore con lo stesso seed o di conservare i dati di test casuali in un file prima di inserirlo nel test, così puoi rieseguire nuovamente il test con esattamente gli stessi dati dell'esecuzione precedente. In questo modo, puoi trasformare qualsiasi test non deterministico in uno deterministico.

EDIT: l'utilizzo di un generatore di numeri casuali per raccogliere alcuni dati di test è IMHO a volte un segno per essere troppo pigro sulla raccolta di buoni dati di test. Invece di lanciare 100.000 valori di test scelti casualmente e sperare che questo sia sufficiente per scoprire tutti i bug gravi per caso, utilizzare meglio il cervello, scegliere da 10 a 20 casi "interessanti" e usarli per la suite di test. Ciò non solo porterà a una migliore qualità dei test, ma anche a prestazioni molto più elevate della suite.


Grazie per la tua risposta. Qual è la tua opinione sul commento che ho fatto alla mia domanda?
DCKing

1
@DCKing: se pensi davvero che un generatore casuale sarà migliore nella scelta di buoni casi di test di te (cosa di cui dubito), usalo una volta per trovare combinazioni di dati di test in cui il tuo programma fallisce e metti quelle combinazioni nella parte "hardcoded" della tua suite di test.
Doc Brown,

Grazie ancora. Aggiornato la mia risposta in modo che non si applichi solo alle app MVC.
DCKing

1
In alcuni contesti dell'interfaccia utente (ad esempio, i giochi che utilizzano l'input del controller) con programmi di test che generano l'immissione casuale di chiavi possono essere utili per lo stress test. Possono scoprire difetti che sono difficili da trovare con input deliberati.
Gort the Robot

@StevenBurnap: bene, il modo in cui capisco la domanda, penso che l'OP avesse in mente test di regressione più convenzionali. Naturalmente, sono d'accordo, lo stress test è un caso speciale che può anche dipendere dall'hardware e comportare comportamenti non deterministici anche quando non si utilizza un generatore casuale. Questo è qualcosa descritto nell'articolo collegato da MichaelT nel primo commento sotto la domanda. E anche negli stress test con input casuali, si può almeno provare a rendere il comportamento più deterministico usando un seme casuale definito.
Doc Brown,

4

Sia deterministico che non deterministico hanno un posto

Le dividerei come segue:

Test unitari.

Questi dovrebbero avere test deterministici e ripetibili con gli stessi stessi dati ogni volta. I test unitari accompagnano sezioni di codice specifiche e isolate e dovrebbero testarle in modo deterministico.

Prove di stress funzionali e di input.

Questi possono usare l'approccio non deterministico con le seguenti avvertenze:

  • tale fatto è chiaramente delineato e richiamato
  • i valori casuali selezionati vengono registrati e possono essere riprovati manualmente

3

Tutti e due.

I test deterministici e non deterministici hanno casi d'uso diversi e valori diversi per la tua suite. Generalmente il non deterministico non può fornire la stessa precisione del test deterministico, che è diventato lentamente "il test non deterministico non fornisce alcun valore". Questo è falso Possono essere meno precisi, ma possono anche essere molto più ampi, il che ha i suoi vantaggi.

Facciamo un esempio: scrivi una funzione che ordina un elenco di numeri interi. Quali sarebbero alcuni dei test unitari deterministici che potresti trovare utili?

  • Un elenco vuoto
  • Un elenco con un solo elemento
  • Un elenco con tutti lo stesso elemento
  • Un elenco con più elementi univoci
  • Un elenco con più elementi, alcuni dei quali sono duplicati
  • Un elenco con NaN, INT_MINeINT_MAX
  • Un elenco che è già parzialmente ordinato
  • Un elenco con 10.000.000 di elementi

E questa è solo una funzione di ordinamento! Certo, potresti sostenere che alcuni di questi non sono necessari o che alcuni di questi possono essere esclusi con un ragionamento informale. Ma siamo ingegneri e abbiamo visto esplodere il ragionamento informale in faccia. Sappiamo di non essere abbastanza intelligenti da comprendere completamente i sistemi che abbiamo costruito o mantenere completamente la complessità nella nostra testa. Ecco perché scriviamo test in primo luogo. L'aggiunta di test non deterministici afferma che potremmo non essere necessariamente abbastanza intelligenti da conoscere a priori tutti i buoni test. Lanciando dati semi-casuali nella tua funzione, hai molte più probabilità di trovare un caso limite che ti sei perso.

Ovviamente, ciò non esclude nemmeno i test deterministici. I test non deterministici aiutano a trovare bug in enormi parti del programma. Una volta trovati i bug, tuttavia, è necessario un modo riproducibile per dimostrare che è stato risolto. Così:

  • Usa test non deterministici per trovare bug nel tuo codice.
  • Utilizzare test deterministici per verificare le correzioni nel codice.

Si noti che ciò significa che molti consigli solidi sui test unitari non si applicano necessariamente ai test non deterministici. Ad esempio, devono essere veloci. I test delle proprietà di basso livello dovrebbero essere veloci, ma un test non deterministico come "simula un utente facendo clic in modo casuale sui pulsanti del tuo sito web e assicurati di non avere mai un errore 500" dovrebbe favorire la completezza rispetto alla velocità. Basta eseguire un test come quello indipendentemente dal processo di compilazione in modo da non rallentare lo sviluppo. Ad esempio, eseguirlo nella propria casella di gestione temporanea privata.


-1

Non vuoi davvero deterministico contro non deterministico.

Quello che potresti desiderare è "sempre lo stesso" vs. "non sempre lo stesso".

Ad esempio, potresti avere un numero di build che aumenta con ogni build e quando vuoi alcuni numeri casuali, inizializzi un generatore di numeri casuali con il numero di build come seed. Quindi ogni build, fai i tuoi test con valori diversi, dandoti più possibilità di trovare bug.

Ma una volta trovato un bug, tutto ciò che devi fare è eseguire il test con lo stesso numero di build ed è riproducibile.


1
Oppure, se non si dispone di un numero di build da utilizzare, posizionare il valore iniziale del seed nell'output dell'esecuzione del test, in modo da poter rieseguire nuovamente i test con lo stesso seed.
Remco Gerlich,
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.