Come progettare un software di gioco in modo che sia facile testare l'unità?


25

È pratico utilizzare un framework di test come JUnit in una situazione di sviluppo del gioco? Che tipo di considerazioni sul design puoi seguire per rendere il tuo gioco più testabile? Quali parti di un gioco possono / dovrebbero essere testate e quali parti dovrebbero / devono essere lasciate ai test umani?

Ad esempio, se il loop di gioco è incapsulato in una funzione, sembra che sarebbe estremamente difficile testarlo. Mi piace riformattare una funzione di "aggiornamento" che richiede un delta temporale e avanza nella logica del gioco; questo consente alcuni trucchi interessanti come la capacità di rallentare il gioco alimentandolo con delta fasulli e più lenti.


6
Perché perdere ore lavorative a scrivere test unitari quando hai un esercito praticamente infinito di lavoro da schiavo per fare test per te? I kid, I kid ...;)
Mike Strobel,

1
In realtà non hai bisogno di bambini, questo è davvero un buon punto! Non ci ho pensato, ma i giochi sono il tipo di software più semplice per far testare altre persone. Immagino che in qualche modo compensi la difficoltà dei test di gioco automatizzati (unità o altro).
Ricket,

3
Il test unitario non implica necessariamente che l'applicazione funzioni correttamente nel suo insieme (vale a dire test funzionali). Si tratta soprattutto di assicurarsi che le modifiche future non rompano la funzionalità esistente. Certo, un utente può vedere quando l'astronave è sottosopra, ma quando l'algoritmo di tracciamento è disattivato di .001, gli effetti potrebbero non essere evidenti fino a quando il gioco non viene giocato per 200 ore. Questo è il tipo di cose che i test unitari possono intercettare prima che il codice esca dalla porta.
Wade Williams,

1
I giochi sono il software più semplice per convincere gli utenti a giocare , ma giocando! = Test . Ottenere buoni rapporti sui bug è piuttosto difficile. Oltre a ciò, rintracciare il punto in cui si verifica un errore nel codice e verificare che le nuove modifiche non rompano il codice esistente beneficiano entrambi in modo significativo dei test automatici.
munifico

Risposte:


25

Uno dei principi di TDD è che in alcuni casi lasci che TDD influenzi il tuo design. Scrivi un test per il sistema, quindi scrivi il codice per far passare quel test, mantieni le dipendenze il più superficiali possibile.

Per me, ci sono solo due cose che non collaudo come parte del test unitario:

Innanzitutto, non collaudo gli elementi visivi e l'aspetto delle cose. Lo collaudo e l'oggetto sarà nel posto giusto dopo l'aggiornamento, che una telecamera eliminerà un oggetto al di fuori dei suoi limiti, che le trasformazioni (almeno quelle eseguite al di fuori degli shader) vengono eseguite correttamente prima di essere consegnate al motore grafico , ma una volta che colpisce il sistema grafico traccio la linea. Non mi piace provare a deridere cose come DirectX.

In secondo luogo, non collaudo davvero la funzione principale del loop di gioco. Metto alla prova che ogni sistema funzionerà quando verrà superato un delta ragionevole e che i sistemi funzioneranno insieme correttamente quando necessario. Quindi aggiorno ogni sistema con il delta corretto nel loop di gioco. Potrei effettivamente avere un test per dimostrare che ogni sistema è stato chiamato con il delta corretto, ma in molti casi lo trovo eccessivo (a meno che tu non stia facendo una logica complessa per ottenere il tuo delta, quindi non è eccessivo).


6
Il primo paragrafo è la chiave. Il fatto è che TDD ti richiede di progettare il tuo codice meglio di quanto avresti senza di esso, semplicemente per consentire il test. Naturalmente molti pensano di essere esperti di design, ma ovviamente quelli che sono più colpiti dalle proprie capacità sono di solito quelli con più da imparare ...
dash-tom-bang

2
Non sarei d'accordo sul fatto che il codice sia necessariamente progettato meglio come risultato. Ho visto un sacco di abominazioni in cui un'interfaccia precedentemente pulita doveva essere aperta e l'incapsulamento rotto per iniettare dipendenze verificabili.
Kylotan,

4
Trovo che ciò accada più nei casi in cui i test sono scritti per classi preesistenti piuttosto che il contrario.
Jeff,

@Kylotan che è probabilmente più un sintomo di design improprio / design di test scadente. Rifattorizza e fai uso di beffe per fare test puliti, non hackerare il tuo codice in uno stato peggiore; questo è l'opposto di ciò che dovrebbe accadere.
Timoxley,

2
L'incapsulamento è una buona cosa, ma la cosa più importante è capire PERCHÉ è una buona cosa. Se lo usi solo per nascondere le dipendenze e una grande brutta interfaccia, praticamente grida che il tuo progetto sotto il cofano probabilmente non è molto bello. L'incapsulamento non consiste principalmente nel nascondere il codice brutto e le dipendenze, ma principalmente nel rendere il codice più facile da capire e per consentire al consumatore di ottenere meno percorsi da seguire quando cerca di ragionare sul codice.
Martin Odhelius,

19

Noel Llopis ha coperto i test unitari nella misura in cui credo che tu stia cercando:

Per quanto riguarda il test dell'intero ciclo di gioco, esiste un'altra tecnica per prevenire regressioni funzionali nel codice. L'idea è di far giocare il tuo gioco tramite un sistema di riproduzione (cioè prima registrando gli input del giocatore e poi riproducendoli su una corsa diversa). Durante la riproduzione, si genera un'istantanea dello stato di ciascun oggetto per ogni fotogramma. Questa raccolta di stati può quindi essere definita "immagine d'oro". Quando si esegue il refactoring del codice, si esegue nuovamente il replay e si confronta lo stato di ciascun fotogramma con lo stato dell'immagine dorata. Se differisce, il test ha avuto esito negativo.

Mentre i vantaggi di questa tecnica sono ovvi, ci sono alcune cose con cui devi fare attenzione:

  • Il tuo gioco deve essere deterministico, quindi devi fare attenzione a cose come dipendere dal tuo orologio di sistema, eventi di sistema / rete, generatori di numeri casuali che non sono seminati in modo deterministico. eccetera.
  • i cambiamenti di contenuto dopo la generazione dell'immagine d'oro possono causare differenze legittime con l'immagine d'oro. Non c'è modo di aggirare questo problema: quando si modificano i contenuti è necessario rigenerare l'immagine d'oro.

+1 Vale anche la pena notare che il valore di questo approccio è direttamente correlato alla lunghezza dell '"immagine d'oro": se non è abbastanza lungo, puoi facilmente perdere i bug; se è troppo lungo, avrai molto da aspettare. È importante provare a far funzionare il gioco forse con un ordine di grandezza più veloce in questa modalità, rispetto alle normali circostanze di runtime, per radere il tempo libero. Saltare il rendering può aiutare!
Ingegnere

9

Sono d'accordo con entrambi i commenti di Jeff e jpaver. Volevo anche aggiungere che l'adozione di un modello di componente per la tua architettura aumenta notevolmente la sua testabilità. Con un modello di componente ogni componente dovrebbe eseguire una singola unità di lavoro e dovrebbe essere testabile in isolamento (o con oggetti finti limitati).

Allo stesso modo, le altre parti del gioco che si basano su entità dovrebbero fare affidamento solo su un sottoinsieme dei componenti per funzionare. In pratica questo significa che di solito puoi deridere alcuni componenti falsi per facilitare il test di queste aree oppure puoi semplicemente comporre entità parziali a scopo di test. ad esempio si tralasciano i componenti di rendering e input perché non sono necessari per testare la fisica.

Infine, vorrei ignorare i consigli sulle prestazioni che ricevi da alcune persone nella comunità di gioco riguardo alle interfacce. Scrivi bene il codice con le interfacce e se riscontri problemi di prestazioni lungo la strada puoi facilmente profilare, identificare e refactor per risolvere il problema. Non sono stato persuaso dalle preoccupazioni di Noel riguardo all'impatto sulle prestazioni delle interfacce o al sovraccarico di complessità che hanno aggiunto.

Detto questo, non esagerare cercando di provare ogni piccola cosa in modo indipendente. Come la maggior parte delle cose, test e progettazione mirano a trovare il giusto equilibrio.


Nota che, sebbene tu non sembri essere nuovo in SX, le risposte non sono ordinate e dire qualcosa come "entrambi i commenti sopra" è destinato a svalutarsi rapidamente, poiché attualmente è che hai un voto prima di uno delle altre due risposte in questo momento. :)
Ricket,

Grazie non ci ho pensato mentre commentavo. Da allora ho aggiornato il commento per selezionare i singoli commenti.
Alex Schearer,

3

Ho pensato di aggiungere una seconda risposta rispondendo al commento del PO che l'utente poteva sostituire i test unitari. Credo che sia completamente sbagliato, poiché lo scopo dei test unitari non è garantire la qualità. Se desideri test in atto per garantire la qualità del tuo programma, probabilmente dovresti studiare i test sugli scenari o investire in un monitoraggio eccezionale.

(Con un ottimo monitoraggio e un prodotto in esecuzione come servizio è davvero possibile inviare alcuni "test" sul cliente, ma è necessario disporre di un sistema sofisticato che rilevi rapidamente gli errori e ripristini le modifiche responsabili. Molto probabilmente questo è troppo per un piccolo team di sviluppo.)

Il vantaggio principale dei test unitari è che costringono lo sviluppatore a scrivere codice meglio progettato. L'atto di test sfida lo sviluppatore a separare le preoccupazioni e incapsulare i dati. Incoraggia inoltre lo sviluppatore a utilizzare interfacce e altre astrazioni in modo da testare solo una cosa.


Accetto, tranne per il fatto che i test unitari non obbligano gli sviluppatori a scrivere un buon codice. C'è un'altra opzione: test scritti davvero male. Hai bisogno di impegno per l'idea di unit test dai tuoi programmatori per evitare che si stancino.
tenpn

2
Certo, ma non è un motivo per buttare via il bambino con l'acqua del bagno. Usa gli strumenti in modo responsabile, ma prima usa gli strumenti.
Alex Schearer,
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.