Test unitari per una biblioteca informatica scientifica


15

Ho avuto un po 'di esperienza con i test unitari in precedenza, in quello che chiamo (non peggiorando) il classico progetto di ingegneria del software: un MVC, con una GUI utente, un database, una logica aziendale nel livello intermedio, ecc. Ora io' sto scrivendo una libreria di calcolo scientifico in C # (sì, so che il C # è troppo lento, uso C, non reinventare la ruota e tutto il resto, ma abbiamo un sacco di persone che fanno calcoli scientifici nella mia facoltà in C #, e ne abbiamo bisogno). È un piccolo progetto, in termini di sviluppo del software, perché lo scrivo principalmente da solo e di tanto in tanto con l'aiuto di alcuni colleghi. Inoltre, non vengo pagato per questo, e soprattutto, è un progetto accademico. Voglio dire, mi aspetto che abbia una qualità professionale un giorno, perché sto pensando di passare all'open source,

Ad ogni modo, il progetto sta diventando grande (circa 18.000 righe di codice, che penso sia grande per il progetto di un solo uomo), e mi sta sfuggendo di mano. Sto usando git per il controllo del codice sorgente e penso di aver fatto abbastanza bene, ma sto testando come una vecchia scuola, intendo, scrivendo applicazioni per console complete che testano gran parte del sistema, principalmente perché non ho idea di come per fare unit test in questo scenario, anche se ritengo sia quello che dovrei fare. Il problema è che la libreria contiene principalmente algoritmi, ad esempio algoritmi grafici, classificatori, risolutori numerici, distribuzioni casuali, ecc. Non so come specificare minuscoli casi di test per ciascuno di questi algoritmi, e poiché molti di essi sono stocastico Non so come convalidare la correttezza. Ad esempio, per la classificazione sono alcune metriche come precisione e richiamo, ma queste metriche sono migliori per confrontare due algoritmi che per giudicare un singolo algoritmo. Quindi, come posso definire la correttezza qui?

Infine c'è anche il problema delle prestazioni. So che è un insieme completamente diverso di test, ma le prestazioni sono una delle caratteristiche importanti di uno strumento scientifico, piuttosto che la soddisfazione dell'utente o altre metriche di ingegneria del software.

Uno dei miei maggiori problemi è con le strutture di dati. L'unico test che posso trovare per un kd-tree è uno stress test: inserisci molti vettori casuali e quindi esegui molte query casuali e confronta con una ricerca lineare ingenua. Lo stesso per le prestazioni. E con gli ottimizzatori numerici, ho funzioni di benchmark che posso testare, ma ancora una volta, questo è uno stress test. Non credo che questi test possano essere classificati come test unitari e, cosa più importante, essere eseguiti continuamente, poiché la maggior parte di essi è piuttosto pesante. Ma penso anche che questi test debbano essere eseguiti, non posso semplicemente inserire due elementi, far apparire il root e sì, funziona per il caso 0-1-n.

Quindi, qual è l'eventuale approccio al test (unitario) per questo tipo di software? E come organizzo i test unitari e quelli pesanti attorno al ciclo code-build-commit-integrate?

Risposte:


19

Direi che il calcolo scientifico è in realtà abbastanza adatto per i test unitari. Hai input e output definiti, pre e postcondizioni chiaramente definiti che probabilmente non cambieranno ogni due settimane in base al capriccio di alcuni progettisti e nessun requisito UI difficile da testare.

Nomini alcuni elementi che potrebbero causare problemi; ecco cosa fare al riguardo:

  • algoritmi randomizzati: ci sono due possibilità. Se vuoi davvero testare la randomizzazione stessa, pianifica un gran numero di ripetizioni e asserisci che la proporzione prevista di casi soddisfa il criterio desiderato, con margini di errore abbastanza grandi che i fallimenti del test spuri saranno abbastanza rari. (Una suite di test che segnala inavvertitamente bug fantasma è molto peggio di quella che non rileva ogni difetto immaginabile.) In alternativa, utilizzare una fonte casuale configurabile e sostituire l'orologio di sistema (o qualunque cosa si stia usando) con una fonte deterministica tramite dipendenza iniezione in modo che i test diventino completamente prevedibili.
  • algoritmi definiti solo in termini di precisione / richiamo: nulla ti impedisce di inserire un intero set di casi di input e misurare la precisione e il richiamo aggiungendoli tutti; si tratta solo di generare semi-automaticamente tali casi di test in modo efficiente in modo che fornire i dati dei test non diventi il ​​collo di bottiglia per la produttività. In alternativa, specificare alcune coppie input / output scelte con giudizio e affermare che l'algoritmo calcola esattamente l'input desiderato può funzionare anche se la routine è abbastanza prevedibile.
  • requisiti non funzionali: se la specifica fornisce realmente requisiti espliciti di spazio / tempo, in pratica è necessario eseguire intere suite di coppie input / output e verificare che l'utilizzo delle risorse sia conforme approssimativamente al modello di utilizzo richiesto. Il trucco qui è quello di calibrare prima la propria classe di test, in modo da non misurare dieci problemi con dimensioni diverse che finiscono per essere troppo veloci da misurare o che impiegano così tanto tempo che l'esecuzione della suite di test diventa poco pratica. Puoi persino scrivere un piccolo generatore di casi d'uso che crea casi di test di dimensioni diverse, a seconda della velocità con cui la PU è in esecuzione.
  • test veloci e lenti: che si tratti di test unitari o di integrazione, spesso si ottengono molti test molto veloci e alcuni molto lenti. Dato che eseguire i test regolarmente è molto prezioso, di solito seguo il percorso pragmatico e separo tutto ciò che ho in una suite veloce e una lenta, in modo che quella veloce possa essere eseguita il più spesso possibile (sicuramente prima di ogni commit), e non importa se due test "semanticamente" si uniscono o no.

+1. Grazie mille, c'è molto se approfondimento nella tua risposta. Solo un paio di domande: che ne dici di algoritmi di ottimizzazione come la meta-euristica. Ho un sacco di funzioni di benchmark, ma tutto quello che posso fare è confrontare due diversi algoritmi. Devo trovare anche un algoritmo di benchmark? Cosa significa che un algoritmo genetico è corretto? E come testare ciascuna delle strategie "parametrizzabili", come il tipo di ricombinazione e mutazione, ecc.?
Alejandro Piad,

1
Per la meta-euristica, mi accontenterei di scegliere alcune coppie I / O caratteristiche, ovvero i "famosi successi" della routine, e verificare che il metodo (o il migliore dei due) trovi effettivamente questa soluzione. I problemi di "raccolta delle ciliegie" che funzionano bene sono ovviamente un no-no nella ricerca sull'ottimizzazione, ma per i test del software che non sono un problema: non stai affermando la qualità dell'algoritmo, solo l'implementazione corretta. Questa è l'unica "correttezza" che puoi provare. Per quanto riguarda la moltiplicazione delle routine parametrizzabili: sì, temo che richieda una quantità combinatoria di test ...
Kilian Foth,

Quindi è come progettare un punto di riferimento banale che tutte le implementazioni corrette dovrebbero risolvere esattamente? C'è un modo per dimostrare la qualità dell'algoritmo? So che non riesco a definire uno standard di qualità per la maggior parte del tempo, ma almeno potrei desiderare che nessun cambiamento diminuisca la qualità raggiunta?
Alejandro Piad,
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.