Come a TDD vengono restituiti i risultati corretti

12

Sto iniziando un nuovo progetto e sto cercando molto duramente di usare TDD per guidare il design. Ho insistito per anni e alla fine ho ottenuto l'approvazione per dedicare il tempo extra a questo progetto per usarlo mentre imparo come farlo correttamente.

Questo è un nuovo modulo, da collegare a un sistema esistente. Attualmente, tutti gli accessi ai dati avvengono tramite servizi web, che per la maggior parte sono solo un sottile involucro sulle procedure memorizzate nel database.

Un requisito è che per un determinato negozio, restituisco tutti gli ordini di acquisto che sono considerati validi per questa applicazione. Un PO è considerato valido se la data di spedizione cade con un determinato intervallo dalla data di apertura dei negozi (questo è per i nuovi negozi).

Ora, non posso inserire questa logica nel codice dell'applicazione, poiché non ho intenzione di riportare un milione di PO solo per ottenere la dozzina che si applica a potrebbe applicarsi a questo negozio dato il vincolo di cui sopra.

Stavo pensando che potrei passare l'intervallo di date a un proc di GetValidPOs e utilizzarlo per restituire gli ordini di acquisto validi. Ma, cosa succede se aggiungiamo un altro requisito a ciò che è considerato un ordine di acquisto valido?

E come posso testarlo e verificare che funzioni? Non stiamo usando un ORM, ed è improbabile che accada. E non posso chiamare il DB nel mio test.

Sono bloccato.

Il mio altro pensiero, sono alcuni mock che restituiscono dati validi, altri che restituiscono dati non validi e fanno in modo che il repository locale generi un'eccezione se si verificano dati non validi e verificano che l'eccezione venga generata se i dati non validi vengono restituiti da GetValidPOs proc (o il mock utilizzato nei test).

Ha senso? O c'è un modo migliore?

UPDATE: Sono in grado di usare EF sembrerebbe. Ora ho solo bisogno di capire come usarlo e renderlo testabile, pur essendo ancora in grado di fare affidamento su stored procedure e la difficoltà di avere dati sparsi su diversi database.

    
posta CaffGeek 04.05.2012 - 17:37
fonte

3 risposte

7

Questo è uno dei principali svantaggi delle stored procedure nell'era del TDD. Hanno dei veri aspetti positivi, anche adesso, ma per definizione qualsiasi test che eserciti un proc memorizzato non è un test unitario; è un test di integrazione al meglio.

La soluzione usuale, supponendo che l'architettura non possa cambiare l'uso di un ORM, è di non mettere questi test nella suite di test delle unità; invece, inserire i test in una suite di integrazione. È comunque possibile eseguire il test ogni volta che si desidera verificare che funzioni, ma poiché il costo inerente alla configurazione del test (inizializzazione di un DB con i dati di test appropriati) è elevato e tocca risorse, l'agente di test dell'unità di build-bot potrebbe non avere accesso a, non dovrebbe essere nella suite di test delle unità.

È ancora possibile testare il codice che richiede i dati, estraendo tutto ciò che non è possibile testare (classi ADO.NET) in una classe DAO che è quindi possibile prendere in giro. È quindi possibile verificare che le chiamate previste vengano effettuate consumando codice e riproducendo il comportamento del mondo reale (ad esempio non trovando risultati) consentendo di testare vari casi d'uso. Tuttavia, l'effettiva impostazione di SqlCommand per chiamare il proc memorizzato è praticamente l'ultima cosa che è possibile testare unitamente, interrompendo la creazione del comando dall'esecuzione del comando e prendendo in giro il comando executor. Se questo suona come un sacco di separazione delle preoccupazioni, può essere; ricorda, "non c'è alcun problema che non possa essere risolto da un altro strato di riferimento indiretto, tranne che per avere troppi strati di riferimento indiretto". Ad un certo punto devi dire "abbastanza: non riesco a testare l'unità, lo faremo in integrazione".

Altre opzioni:

  • Verifica il processo memorizzato utilizzando un'istanza DBMS "di breve durata" come SQLite. Solitamente è più semplice farlo quando si utilizza un ORM, ma il test può essere eseguito "in memoria" (o con un file di database preimpostato incluso nella suite di test). Ancora non è un test unitario, ma può essere eseguito con un alto grado di isolamento (il DBMS è parte del processo in esecuzione, e non qualcosa che si connette a distanza che potrebbe trovarsi nel mezzo della suite di test in conflitto di qualcun altro). Lo svantaggio è che le modifiche al processo memorizzato possono avvenire in produzione senza che il test rifletta la modifica, quindi devi essere disciplinato per assicurarti che la modifica venga apportata per la prima volta in un ambiente di test.

  • Considera l'aggiornamento a un ORM. Un ORM con un provider Linq (praticamente tutti quelli di uso comune ne hanno uno) consentirebbe di definire la query come un'istruzione Linq; tale affermazione può quindi essere fornita a un repository fittizio che ha una raccolta in memoria di dati di test per applicarla a. È quindi possibile verificare che la query sia corretta senza nemmeno toccare il DB (è comunque necessario eseguire la query in un ambiente di integrazione, per verificare che il provider Linq possa digerire correttamente la query).

risposta data 04.05.2012 - 18:23
fonte
4

Il mio consiglio è di divide and conquer . Dimentica il database e la persistenza per il momento e concentrati sulla verifica delle false implementazioni dei tuoi repository o degli oggetti di accesso ai dati.

Now, I can't put this logic in the application code, as I'm not going to bring back a million POs just to get the dozen that apply to could apply to this store given the constraint above.

Prendo in giro il repository che restituisce gli ordini di acquisto. Creare una simulazione con venti ordini di acquisto dispari.

I was thinking, I could pass the date range to a GetValidPOs proc, and have it use those values to return the valid POs. But, what if we add another requirement to what is considered a valid PO?

Interrompi una chiamata a GetValidPOs in modo che chiami la tua simulazione, piuttosto che la procedura del database.

And how do I test this and verify it keeps working? We are not using an ORM, and it's unlikely to happen. And I can't call the DB in my test.

Hai bisogno di un test unitario per assicurarti che i dati corretti vengano restituiti da una simulazione.

È inoltre necessario un test di integrazione per assicurarsi che i dati corretti vengano restituiti da un database. Il test di integrazione richiederebbe una certa configurazione e pulizia. Ad esempio, prima di eseguire il test di integrazione, inizializzare il database eseguendo uno script. Verifica che il tuo script abbia funzionato. Interrogare il database chiamando le stored procedure. Verifica che i risultati siano corretti. Pulisci il database.

My other thought, is have some mocks that return valid data, others that return some bad data, and have the local repository throw an exception if bad data occurs, and test that the exception gets thrown if invalid data is returned by GetValidPOs proc (or the mock used in testing).

Come ho già detto, hai bisogno di una simulazione che restituisca almeno alcuni dati che puoi interrogare.

Quando si interrogano i dati si desidera assicurarsi che il sistema sia in grado di gestire le eccezioni con garbo. Pertanto simuli il comportamento in modo da generare eccezioni in determinati scenari. Quindi scrivi i test per assicurarti che il tuo sistema possa gestire queste eccezioni con garbo.

    
risposta data 05.05.2012 - 11:04
fonte
0

Proprio come il test unitario di Java o Javascript significa scrivere test unitari usando il linguaggio Java per java, e test di unità con le funzioni Javascript con Javascript, scrivere test automatici per guidare a scrivere stored procedure significa che la libreria di test unitari che stai cercando è basato su stored procedure.

Detto in un altro modo, usa le stored procedure per testare le stored procedure perché:

  • dal momento che stai sviluppando nella lingua della procedura, dovresti avere l'abilità di scrivere i tuoi test nella lingua della procedura
  • test di scrittura nella tua lingua di procedura aumenteranno le tue abilità nella lingua della procedura che a sua volta aiuterà lo sviluppo del tuo prodotto
  • avrai accesso diretto a tutti gli strumenti forniti dal DB e potrai utilizzare quegli strumenti anche per mantenere i tuoi test di unità il più semplici possibile
  • unit test che sono memorizzati nello stesso DB come le procedure che stanno testando saranno veloci (cosa più vicina alla unit test come velocità) perché non stai attraversando i confini del sistema

Proprio come TDD in una lingua OO, vuoi che il tuo test dell'unità imposti solo una fila di dati per testare ciò di cui ha bisogno per la procedura (minimalismo, solo quello di cui hanno bisogno i test semplici). Il risultato di questo è che avrete diversi test unitari semplici per ciascuna stored procedure. Questi semplici test saranno più facili da gestire rispetto a test complicati che dipendono da un set di dati di grandi dimensioni che non si ricollega facilmente a ciò di cui il test ha effettivamente bisogno.

    
risposta data 27.05.2016 - 20:00
fonte

Leggi altre domande sui tag