Come testare i calcoli che vengono solo passati nello stack anziché restituiti verso l'alto

7

Questo è un problema che ho cercato di riprendere da un paio di mesi. È apparso di nuovo in un progetto di apprendimento a cui stavo lavorando ieri sera, quindi lo userò come esempio.

Sto costruendo un sistema di componenti di entità e un sistema di movimento che controlla il movimento di ogni entità in ogni turno.

class MovementSystem {
     private:
         vector<PositionComp> positions;
         vector<MoveAIComp> moveAIs;

         vector<vector<bool> > generateMap() 
         { 
            // Cycle through all positions and generates a 2D array
            // detailing whether each map space is occupied or not
         }

         void updateComps(vector<vector<bool> > map)
         {
            // For each entity generates a 3x3 array from map, centered
            // at that entity's position, passes it to 
            // moveAI.getMove(surroundings), then updates the position
         }
     public:
         void update(Turn turn)
         {
              map = this->generateMap();
              this->updateComps(map);
         }
};

Ora il collaudo di alcune di queste funzionalità è abbastanza facile. Aggiungo una posizione e sposta il componente AI nel sistema. Il componente moveAI è un oggetto fittizio che restituisce una specifica mossa che ho specificato nel test, quindi richiama l'aggiornamento sul sistema e mi assicuro che la posizione modificata nel modo in cui si supponeva fosse.

Il problema con questo è che ho saltato l'intera chiamata a generateMap() e la prima parte di updateComps che crea l'array 3x3. Certo, ho verificato che la posizione sia correttamente aggiornata in base alla decisione presa da moveAI, ma non ho verificato che moveI abbia le informazioni corrette per prendere questa decisione.

Finora ho trovato alcuni modi per ovviare a questo:

  1. Cambia il mio mock MoveAIComponent per memorizzare gli input in getMove , così posso verificarli anche nel test.
  2. Il mio MovementSystem potrebbe fare troppo, quindi dovrei dividerlo in due, un MapSystem che genera la mappa e aggiorna un nuovo SurroundingsComponent , quindi cambiare MovementSystem per usare i dintorni, la posizione e moveAI, consentendo test su MapSystem per coprire tale funzionalità.
  3. Lascia il test di quella funzionalità per i test di integrazione, quando userò oggetti MoveAIComponent reali che si preoccupano dell'ambiente.

Nessuna di queste opzioni mi piace particolarmente. 1. aumenta notevolmente la complessità sia dei miei test che della mia classe mock in quanto ho bisogno di aggiungere degli asserzioni per garantire che l'input sia corretto e mettere in cache ogni chiamata agli oggetti mock. 2. si sente come l'interfaccia gonfia. Poiché surroundings è utilizzato solo all'interno di MovementComponent , perché dovrei creare componenti extra longevi per tracciare quelli che sono dati temporanei, transitori e di uso singolo? E infine 3. Non mi piace per lo stesso motivo di 1, tranne peggio. I test potrebbero ora fallire per due motivi: il surroundings non è corretto quando viene passato a moveAI o il moveAI sta prendendo una decisione inaspettata.

Quindi, più in generale, quando si ha una classe che genera dati che vengono passati solo più in profondità nello stack e non viene mai restituita verso l'alto, quale miglior modo per testare tale funzionalità. Ovviamente sono aperto a suggerimenti / ridisegni a cui non ho pensato.

    
posta Godric Seer 04.03.2016 - 16:58
fonte

1 risposta

7

Innanzitutto, l'opzione 2 non è così male potrebbe essere per te. Avere due componenti separati, disaccoppiati A e B con un flusso di dati da A - > B tramite un'interfaccia pubblica consente di testarli singolarmente o di "collegare" una sorta di componente del monitor M tra loro "A- > M- > B" per scopi di test o di registrazione.

Tuttavia, se non ti piace questo approccio, potresti invece aggiungere un oggetto monitor (opzionale) alla tua classe, solo a scopo di test, che può essere iniettato dall'esterno. Il monitor fornisce un metodo che ottiene l'input di getMove e lo restituisce immediatamente, e lo usi in questo modo:

    moveAI.getMove(monitor.log(surroundings))

Rendere l'implementazione predefinita del monitor una cosa banale che passa semplicemente attraverso l'input. A scopo di test, si aggiunge un monitor "reale" che memorizza nella cache l'input di getMove. In questo modo, puoi mantenere tale funzionalità fuori dal tuo MoveAIComponent mock.

Per le classi complesse, è una tecnica ben nota e pulita per fornire "botole di manutenzione" a scopo di test. L'oggetto monitor è un esempio per questo. Non aver paura di usare questa tecnica perché vorrà apportare alcune modifiche alla tua classe per renderla più testabile.

    
risposta data 04.03.2016 - 17:25
fonte

Leggi altre domande sui tag