Come si applica TDD alle funzioni di lettura / scrittura?

11

Sembra un problema di pollo e uova.

Puoi fare in modo che una funzione di scrittura scriva in qualche archivio dati, ma non sai mai che l'hai salvata correttamente senza una funzione di lettura testata.

Puoi far leggere una funzione di lettura da un archivio dati, ma come puoi inserire elementi in quell'archivio dati, da leggere, senza una funzione di scrittura testata?

EDIT:

Mi sto connettendo a e faccio transazioni con un database SQL per salvare e caricare oggetti per l'uso. Non ha senso testare le funzioni di accesso fornite dal DB, ma avvolgo tali funzioni DB per serializzare / deserializzare gli oggetti. Voglio essere sicuro che sto scrivendo e leggendo le cose giuste da e verso il DB correttamente.

Non è come aggiungere / eliminare, come cita @snowman. Voglio sapere che i contenuti che ho scritto sono corretti, ma che richiede una funzione di lettura ben testata. Quando leggo, voglio essere sicuro che la mia lettura abbia creato correttamente un oggetto uguale a ciò che è stato scritto; ma ciò richiede una funzione di scrittura ben testata.

    
posta user2738698 06.03.2017 - 18:42
fonte

7 risposte

7

Inizia con la funzione di lettura.

  • Nell'impostazione di prova : crea il database e aggiungi dati di test. tramite script di migrazione o da un backup. Poiché questo non è il tuo codice, non richiede un test in TDD

  • Nel test : instanzia il tuo repository, punta al db del tuo test e chiama il metodo Read. Verifica che i dati del test vengano restituiti.

Ora hai una funzione di lettura completamente testata, puoi passare alla funzione Write, che può usare la lettura già esistente per verificare i propri risultati

    
risposta data 07.03.2017 - 13:54
fonte
6

Spesso faccio solo una scrittura seguita da una lettura. per esempio. (Pseudocodice)

Foo foo1 = setup some object to write
File tempfile = create a tempfile, possibly in memory 
writeFoo(foo1, tempfile) 
Foo foo2 = readFoo(tempfile) 
assertEquals(foo1, foo2); 
clean-up goes here

Aggiunto più tardi

Oltre a questa soluzione che è "prgamatica" e "abbastanza buona", si potrebbe sostenere che le altre soluzioni testano la cosa sbagliata . Verificare se le stringhe o le istruzioni SQL corrispondono non è un'idea terribile, l'ho fatto io stesso, ma sta testando un effetto collaterale ed è fragile. Cosa succede se si modifica la maiuscola, si aggiunge un campo o si aggiorna un numero di versione all'interno dei dati? Cosa succede se il tuo driver SQL cambia l'ordine delle chiamate per l'efficienza oppure il serializzatore XML aggiornato aggiunge uno spazio aggiuntivo o modifica una versione dello schema?

Ora, se devi aderire molto strettamente ad alcune specifiche ufficiali, allora sono d'accordo sul fatto che il controllo dei minimi dettagli sia appropriato.

    
risposta data 06.03.2017 - 19:41
fonte
3

Non farlo. Non eseguire il test I / O dell'unità. È una perdita di tempo.

Logica test unità. Se c'è molta logica da testare nel codice I / O, dovresti rifattorizzare il tuo codice per separare la logica di come fai I / O e cosa I / O fai dall'effettivo business di fare I / O (che è praticamente impossibile da testare).

Per elaborare un po ', se si desidera testare un server HTTP, è necessario farlo attraverso due tipi di test: test di integrazione e test unitari. I test unitari non dovrebbero interagire con I / O. Questo è lento e introduce molte condizioni di errore che non hanno nulla a che fare con la correttezza del tuo codice. I test unitari non dovrebbero essere soggetti allo stato della tua rete!

Il tuo codice dovrebbe essere separato:

  • La logica per determinare quali informazioni inviare
  • La logica per determinare quali byte inviare per inviare un particolare bit di informazioni (come faccio a codificare una risposta ecc. in byte non elaborati) e
  • Il meccanismo per scrivere effettivamente quei byte su un socket.

I primi due riguardano la logica e le decisioni e richiedono test unitari. L'ultimo non comporta l'assunzione di molte se nessuna decisione e può essere testato meravigliosamente utilizzando i test di integrazione.

In realtà questo è solo un buon design in generale, ma una delle ragioni è che rende più facile il test.

Ecco alcuni esempi:

  • Se si sta scrivendo il codice che ottiene i dati da un database relazionale, è possibile testare unitamente come si mappano i dati restituiti dalle query relazionali al modello dell'applicazione.
  • Se si sta scrivendo un codice che scrive dati in un database relazionale, è possibile testare unitamente le parti di dati che si desidera scrivere nel database senza testare effettivamente le query SQL specifiche utilizzate. Ad esempio, è possibile conservare due copie dello stato dell'applicazione in memoria: una copia che rappresenta l'aspetto del database e la copia di lavoro. Quando si desidera eseguire la sincronizzazione con il database, è necessario eseguire una nuova ricerca e scrivere le differenze nel database. Puoi facilmente testare il codice diff.
  • Se si sta scrivendo un codice che legge qualcosa da un file di configurazione, si desidera testare il parser del formato del file di configurazione, ma con le stringhe del file sorgente del test piuttosto che le stringhe che si ottengono dal disco.
risposta data 07.03.2017 - 21:33
fonte
2

Non so se si tratta di una pratica standard o meno, ma funziona bene per me.

Nelle mie implementazioni del metodo di scrittura in lettura non di database io uso i miei metodi toString() e fromString() specifici del tipo come dettagli di implementazione.

Questi possono essere facilmente testati in isolamento:

 assertEquals("<xml><car type='porsche'>....", new Car("porsche").toString());

Per i metodi di lettura di lettura effettivi ho un test di integrazione che legge e scrive fisicamente in un test

A proposito: C'è qualcosa che non va con un test che prova leggere / scrivere insieme?

    
risposta data 06.03.2017 - 19:18
fonte
1

I dati noti devono essere formattati in modo noto. Il modo più semplice per implementare questo è usare una stringa costante e confrontare il risultato, come descritto da @ k3b.

Non sei limitato alle costanti però. Potrebbero esserci diverse proprietà dei dati scritti che è possibile estrarre utilizzando un diverso tipo di parser, ad esempio espressioni regolari o anche sonde ad hoc che cercano funzionalità dei dati.

Per quanto riguarda la lettura o la scrittura dei dati, può essere utile disporre di un file system in memoria che consente di eseguire i test senza la possibilità di interferenze da altre parti del sistema. Se non hai accesso a un buon file system in memoria, utilizza un albero di directory temporaneo.

    
risposta data 06.03.2017 - 19:28
fonte
1

Utilizza l'iniezione delle dipendenze e il mocking.

Non vuoi testare il tuo driver SQL e non vuoi testare se il tuo database SQL è online e configurato correttamente. Sarebbe parte di un test di integrazione o di sistema. Vuoi testare se il tuo codice invia le istruzioni SQL che dovrebbe inviare e se interpreta le risposte nel modo previsto.

Quindi quando hai un metodo / una classe che dovrebbe fare qualcosa con un database, non farlo ottenere da solo quella connessione al database. Cambialo in modo che l'oggetto che rappresenta la connessione al database sia passato ad esso.

Nel codice di produzione, passa l'oggetto del database effettivo.

Nei test delle unità, passare un oggetto fittizio che si comporta come un vero database non contatta effettivamente un server di database. Basta controllare se riceve le istruzioni SQL che dovrebbe ricevere e quindi risponde con le risposte hardcoded.

In questo modo puoi testare il tuo livello di astrazione del database senza nemmeno bisogno di un vero database.

    
risposta data 07.03.2017 - 16:51
fonte
0

Se utilizzi un mappatore relazionale di oggetti, di solito è disponibile una libreria associata per verificare che i mapping funzionino correttamente creando un aggregato, continuando a conservarlo e ricaricandolo da una nuova sessione, seguito dalla verifica di lo stato rispetto all'oggetto originale.

NHibernate offre Test delle specifiche di persistenza . Può essere configurato per funzionare contro un archivio in memoria per i test rapidi delle unità.

Se segui la versione più semplice dei modelli di Repository e Unit of Work e collaudi tutti i tuoi mapping, puoi contare su cose che funzionano in modo abbastanza efficace.

    
risposta data 07.03.2017 - 21:59
fonte

Leggi altre domande sui tag