Gli oggetti fittizi per i test devono essere creati a un livello alto o basso

2

Quando si creano test di unità per questi altri oggetti, qual è il modo migliore per creare oggetti mock che forniscono dati ad altri oggetti. Dovrebbero essere creati ad un 'alto livello' e intercettare le chiamate il prima possibile, o dovrebbero essere fatte a un 'basso livello' e quindi fare quanto il codice reale è ancora chiamato?

es. Sto scrivendo un test per un codice che richiede un oggetto NoteMapper che permetta a Notes di essere caricato dal DB.

class NoteMapper {

    function getNote($sqlQueryFactory, $noteID) {
        // Create an SQL query from $sqlQueryFactory
        // Run that SQL 
        // if null 
        //     return null
        // else
        //    return new Note($dataFromSQLQuery)
    }
}

Potrei prendere in giro questo oggetto a un livello elevato creando un oggetto NoteMapper fittizio, in modo che non ci siano affatto chiamate all'SQL, ad es.

class MockNoteMapper {

    function getNote($sqlQueryFactory, $noteID) {
        //$mockData = {'Test Note title', "Test note text" }
        // return new Note($mockData);
    }
}

Oppure potrei farlo a un livello molto basso, creando un MockSQLQueryFactory che invece di interrogare effettivamente il database fornisce solo dati di simulazione e lo passa all'attuale oggetto NoteMapper.

Sembra che creare mock ad alto livello sia più facile a breve termine, ma che a lungo termine farlo a un livello basso sarebbe più potente e, eventualmente, consentire una maggiore automazione dei test, ad es. registrando i dati in un DB esterno e quindi riproducendo i dati per i test.

C'è un modo consigliato di creare mock? Esistono norme rigide e veloci su quali sono le migliori o dovrebbero essere entrambe utilizzate laddove appropriato?

    
posta Danack 31.10.2013 - 02:57
fonte

5 risposte

2

Gli approcci di livello superiore sono più appropriati per i test unitari. Gli approcci di livello inferiore sono più appropriati per i test di integrazione.

L'intero punto di un test unitario è quello di assicurarsi che la singola unità funzioni, quindi si desidera rimuovere la possibilità di un fallimento del test a causa del bug di un altro componente. L'idea che il test di livello inferiore sia più "automatico" è falso. I test unitari potrebbero ancora attingere a una fonte di dati esterna per i proiettori. Il livello di derisione e test è ciò che rende un test unitario, non la fonte dei dati.

Immagina per un minuto che stai creando un componente per trasformare un pezzo di testo proveniente da un database. Qualcosa come public String removeSpaces(Database db) . Puoi archiviare del testo di test nel file A e il risultato atteso nel file B. Il tuo caso di test dovrebbe quindi simulare db restituire il contenuto di A. Dopo aver chiamato il metodo che stai testando, puoi confrontare il valore restituito al contenuto di B. I tuoi dati sono esterni e modificabili senza ricompilare. Potresti anche essere cool e utilizzare una convenzione di denominazione dei file in modo che la lettura di A e B sia più semplice.

Il test di livello inferiore è utile per i test di integrazione. Sono potenti, ma servono anche a uno scopo diverso. Lascerò da solo questo argomento poiché la domanda riguarda principalmente il test delle unità.

    
risposta data 31.10.2013 - 04:38
fonte
2

Il codice dei test unitari di implementazione è il codice che è necessario sviluppare, estendere, correggere e mantenere e si è disposti a farlo correttamente e per evitare un lavoro inutile. Se pianifichi una suite di test per unità di grandi dimensioni, il suo design merita la stessa cura e pensieri rispetto all'applicazione stessa: trovare la giusta organizzazione è altrettanto difficile.

Oltre a questa dichiarazione generale, ecco alcuni aspetti che potresti voler prendere in considerazione. Presumo che tu stia scrivendo un test unitario per una classe Consumer che usa il NoteMapper (questo è il caso basso-beffardo ) o MockNoteMapper (questo è il high-mocking caso ).

  • Nel caso low-mocking case stai effettivamente testando Consumer e NoteMapper , quindi probabilmente vorrai aggiungere un test separato NoteMapper separatamente in modo che il test sia una prova del fatto che Consumer implementa il comportamento corretto.

  • Nel caso low-mocking il NoteMapper potrebbe contenere invarianti nascosti che non sono applicati dal tuo progetto ma dall'implementazione. Nel caso high-mocking ottieni confidenza che la tua implementazione di Consumer sia corretta al punto in cui non utilizza invarianti nascosti di NoteMapper .

  • I test unitari basati su oggetti di simulazione vengono implementati in modo simile ai test di regressione, in cui l'oggetto mocking viene utilizzato per iniettare valori che dimostrano alcuni bug storici nel programma.

risposta data 31.10.2013 - 14:47
fonte
1

Entrambi.

Durante il test degli oggetti che usano NoteMapper , puoi usare la sua simulazione per evitare le chiamate a SQL, e dirgli di fare tutto ciò che vuoi in completa libertà per testare come risponde il SUT esso.

Inoltre, quando si testano le dipendenze di NoteMapper , se si definiscono le aspettative sull'oggetto SQL interno NoteMapper utilizzato, si sta procedendo strettamente alla sua implementazione e si crea anche un gran numero di bozze in ogni test riguardo alle cose davvero non dovrebbe preoccuparsi.

Durante il test di NoteMapper , potresti voler prendere in giro l'SQL in modo da testare solo la logica e non l'effettiva implementazione SQL.

Tuttavia , credo che NoteMapper risulterebbe non fare molto oltre alle query SQL, quindi il test dovrebbe essere effettivamente un test di integrazione, testando l'integrazione tra la sua implementazione e il particolare SQL Implementazione di DB.

    
risposta data 31.10.2013 - 05:26
fonte
0

Suggerirei l'approccio di alto livello. Mock il prima possibile.

In questo modo sei sicuro di ciò che riceverà il tuo metodo e quindi sarai più veloce a tracciare l'errore. Ricorda: vuoi isolare il tuo test unitario da qualsiasi altra cosa. Se prendi in giro il livello basso come hai descritto, testerai non solo il metodo che vuoi testare, ma anche getNote . Questo sarebbe più un test di integrazione o di accettazione.

    
risposta data 31.10.2013 - 03:09
fonte
0

I test unitari sono esattamente questo, per testare una piccola unità di codice. In base alla descrizione del problema, sembra un test dei dati di alto livello e un test di basso livello testerà diverse unità di funzionalità, quindi, per una copertura completa del codice, sarebbe saggio testare entrambi gli scenari.

    
risposta data 31.10.2013 - 05:31
fonte

Leggi altre domande sui tag