Ortogonalità dei test unitari vs. concisione dei test unitari


14

Sto scrivendo unit test per un sistema di guida per un videogioco. Il sistema ha diversi comportamenti (evitare quest'area a causa del motivo A, evitare quest'area a causa del motivo B, ognuno aggiungendo un po 'di contesto a una mappa della regione. Una funzione separata quindi analizza la mappa e produce il movimento desiderato.

Ho problemi a decidere come scrivere i test unitari per i comportamenti. Come suggerisce TDD, sono interessato solo a come i comportamenti influenzano il movimento desiderato. Ad esempio, evitare a causa della ragione A dovrebbe comportare un movimento lontano dalla posizione sbagliata suggerita. In realtà non mi interessa come o perché il comportamento aggiunge contesto alla mappa, solo che il movimento desiderato è lontano dalla posizione.

Quindi i miei test per ogni comportamento impostano il comportamento, lo fanno scrivere sulla mappa, quindi esegue la funzione di analisi della mappa per elaborare il movimento desiderato. Se quel movimento soddisfa le mie specifiche, allora sono felice.

Tuttavia ora i miei test dipendono da entrambi i comportamenti che funzionano correttamente e che la funzione di analisi della mappa funziona correttamente. Se la funzione di analisi fallisce, allora otterrei centinaia di test falliti piuttosto che un paio. Molte guide per la scrittura di test suggeriscono che questa è una cattiva idea.

Tuttavia, se provo direttamente contro l'output dei comportamenti deridendo la mappa, allora sicuramente mi sto accoppiando troppo strettamente all'implementazione? Se riesco a ottenere lo stesso movimento desiderato dalla mappa usando un comportamento leggermente diverso, i test dovrebbero comunque passare.

Quindi ora soffro di dissonanza cognitiva. Qual è il modo migliore per strutturare questi test?


... non sono sicuro che ci sia una risposta magica. Fondamentalmente testare cose che possono rompersi. Così idealmente potresti in qualche modo magicamente essere in grado di dire quali test di basso livello sarebbero già stati sufficientemente coperti da test di alto livello da non aver bisogno dei loro test unitari di livello inferiore. Un altro modo di osservarlo è: dal momento in cui un test fallisce al momento in cui uno sviluppatore risolve il problema, quanto tempo passerà? Vuoi che questa volta sia basso. Se non si eseguissero unità, ma solo test funzionali perfetti (copertura di alto livello), quel tempo sarebbe ancora troppo alto. Prova a usarlo come guida euristica.
Calphool,

Risposte:


10

Nel mondo ideale, avresti davvero una serie di test unitari perfettamente ortogonali, tutti sullo stesso livello di astrazione.

Nel mondo reale, di solito hai test su molti livelli diversi dell'app, quindi spesso i test di livello superiore esercitano funzionalità che è già stata testata anche da test di livello inferiore dedicati. (Molte persone preferiscono chiamare tali sottosistemi di test di livello superiore / test di integrazione piuttosto che test unitari; tuttavia possono ancora essere eseguiti sullo stesso framework di test unitari, quindi dal punto di vista tecnico non c'è molta differenza.)

Non penso sia una brutta cosa. Il punto è che il tuo codice sia testato nel modo migliore che si adatta al tuo progetto e alla tua situazione, per non aderire al "modo ideale".


Immagino che il punto principale dell'autore fosse se dovessi usare stub / mock o implementazioni reali per questi test di livello superiore
SiberianGuy

2

Questo tipo di test è il motivo per cui sono state inventate le beffe. L'idea principale: scrivi un finto per il tuo oggetto (mappa, comportamento, personaggio, ...), quindi scrivi i test usando quel finto invece dell'oggetto reale. Le persone a volte chiamano beffe di mozziconi e credo che ci siano altre parole per entrambi.

Nel tuo caso, scriveresti una derisione per la mappa ogni volta che devi testare i comportamenti e altre derisioni per i comportamenti ogni volta che vuoi testare la mappa. Idealmente, le tue beffe sarebbero molto più semplici dei comportamenti reali che stai deridendo, incorporando solo i metodi o le variabili di cui hai effettivamente bisogno per quel test. Potrebbe essere necessario scrivere una derisione diversa per ogni test, oppure potrebbe essere possibile riutilizzare alcune simulazioni. Indipendentemente da ciò, dovrebbero essere adatti per il test e non dovrebbero essere il più possibile simili al comportamento reale.

Se includessi alcuni esempi di mappe o comportamenti, forse qualcuno potrebbe fornire esempi delle simulazioni che potresti scrivere. Non io, poiché non ho mai programmato un videogioco più avanzato di Pong, e anche allora stavo seguendo un libro, ma forse qualcuno era esperto di test unitari e sviluppo di giochi.


0

Penso che provi a testare cose di livello molto più alto di un'unità.

La maggior parte dei test comportamentali richiederebbe un po 'di intelligenza dietro di esso, per capire se si è comportato correttamente. Questo non può essere fatto facilmente usando test automatici.


questo è il motivo per cui ho menzionato la concisione. Posso facilmente scrivere test di piccole unità per testare singole righe di codice all'interno di un comportamento e l'ho fatto, ma dipende dalla lettura dell'output della funzione di analisi della mappa. Nessuno dei miei test comportamentali è grande, ognuno di essi verifica solo una parte della funzionalità del comportamento.
1011
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.