Come TDD restituire i risultati corretti


12

Sto iniziando un nuovo progetto e sto provando molto duramente a usare TDD per guidare il progetto. Ho spinto per anni e finalmente ho ottenuto l'approvazione per dedicare del 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 webservices, che per la maggior parte sono solo un wrapper sulle procedure memorizzate nel database.

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

Ora, non riesco a inserire questa logica nel codice dell'applicazione, poiché non riporterò un milione di PO solo per ottenere la dozzina a cui applicare potrebbe applicare a questo archivio dato il vincolo sopra.

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

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

Sono bloccato.

L'altro mio pensiero, è avere alcune beffe che restituiscono dati validi, altre che restituiscono alcuni dati errati e che il repository locale genera un'eccezione se si verificano dati errati e verifica che l'eccezione venga generata se GetValidPOs proc restituisce dati non validi (o la finta usata nei test).

ha senso? O c'è un modo migliore?

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


Per curiosità, perché non puoi selezionare solo i PO validi con una semplice istruzione SQL? (Questa domanda o la risposta non implica una soluzione.)
scarfridge

Risposte:


7

Questo è il principale svantaggio delle procedure memorizzate nell'era del TDD. Hanno dei veri vantaggi, anche adesso, ma per definizione qualsiasi test che esercita un proc memorizzato non è un test unitario; nel migliore dei casi è un test di integrazione.

La solita soluzione, supponendo che l'architettura non possa cambiare per usare un ORM invece, è quella di non mettere questi test nella suite di test unitari; metti invece 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 corretti) è elevato e tocca le risorse che l'agente di test unit del robot del build-bot potrebbe non avere accesso a, non dovrebbe essere nella suite di unit test.

È ancora possibile testare il codice unitario che richiede i dati, astraggendo tutto ciò che non è possibile testare l'unità (classi ADO.NET) in una classe DAO che è quindi possibile deridere. È quindi possibile verificare che le chiamate previste vengano effettuate consumando codice e riprodurre il comportamento del mondo reale (come non trovare risultati) che consenta di testare vari casi d'uso. Tuttavia, l'effettiva configurazione di SqlCommand per chiamare il proc memorizzato è praticamente l'ultima cosa che è possibile testare l'unità, interrompendo la creazione del comando dall'esecuzione del comando e deridendo l'esecutore del comando. Se questo sembra un sacco di separazione delle preoccupazioni, può essere; ricordate, "non vi è alcun problema che non può essere risolto da un altro livello di riferimento indiretto, tranne che per avere troppi livelli di riferimento indiretto". Ad un certo punto devi dire "abbastanza; non riesco proprio a testarlo, noi '

Altre opzioni:

  • Testare il proc memorizzato usando un'istanza DBMS "di breve durata" come SQLite. Di solito è più semplice farlo quando si utilizza un ORM, ma il test può quindi essere eseguito "in memoria" (o con un file di database preimpostato incluso con la suite di test). Non è ancora un unit test, ma può essere eseguito con un alto grado di isolamento (il DBMS fa parte del processo in esecuzione e non qualcosa a cui ti connetti in remoto che potrebbe trovarsi nel mezzo della suite di test in conflitto di qualcun altro). Il rovescio della medaglia è che le modifiche al proc memorizzato possono avvenire in produzione senza che il test rifletta il cambiamento, quindi è necessario essere disciplinati nell'accertarsi che la modifica venga prima apportata in un ambiente di test.

  • Prendi in considerazione 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 istruzione può quindi essere data a un repository beffardo che ha una raccolta in memoria di dati di test a cui applicarlo. È 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).


2
-1 perché TDD! = Test unitario. Perfettamente perfetto per includere test a livello di integrazione quando si fa TDD.
Steven A. Lowe,

Il test unitario è un sottoinsieme dello sviluppo guidato dai test. In uno sviluppo guidato da test, si creerebbe uno scheletro ambulante del proprio sistema e quindi si eseguiranno test di unità, integrazione e funzionamento su quel sistema. I test di integrazione, unità o accettazione falliscono, quindi li fai passare e scrivi ulteriori test.
CodeART

1
Capisco tutto questo, entrambi. Dove ho detto che doveva essere un test di integrazione che significava che non si poteva TDD? Il mio punto era che una procedura memorizzata non può essere testata isolatamente, il che è qualcosa che vuoi fare per quanto più possibile della tua base di codice. Il test di SP richiede invece test di integrazione più complessi e di maggiore durata; mentre sono ancora migliori dei test manuali, una suite di test ad alta integrazione può richiedere ore per l'esecuzione e può avere un effetto dannoso sugli sforzi di CI.
KeithS,

I test SP spesso richiedono anche un set di dati specifico nel database dei test; il codice per riportare il database nello stato corretto per ottenere i risultati previsti è molto spesso più LoC e molte volte più lungo del codice che si sta effettivamente esercitando. Ciò aggrava ulteriormente la complessità temporale della suite di test e spesso l'impostazione deve essere ripetuta per ogni singolo test (e probabilmente dovrebbero essercene diversi per ogni SP, per verificare che ogni requisito funzionale della query all'interno sia soddisfatto).
KeithS,

Le procedure memorizzate possono essere testate separatamente. In quale altro modo verrebbero validati? Per Transact SQL c'è tSQLt ( tsqlt.org )
kevin cline,

4

Il mio consiglio è di dividere e conquistare . Dimentica il database e la persistenza per il momento e concentrati sul test di implementazioni false dei tuoi repository o oggetti di accesso ai dati.

Ora, non riesco a inserire questa logica nel codice dell'applicazione, poiché non riporterò un milione di PO solo per ottenere la dozzina a cui applicare potrebbe applicare a questo archivio dato il vincolo sopra.

Vorrei prendere in giro il repository che restituisce gli ordini di acquisto. Crea un finto con venti ordini di acquisto dispari.

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

Instradare una chiamata a GetValidPOs in modo che chiami la procedura fittizia, anziché la procedura del database.

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

È necessario un test unitario per assicurarsi 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 un po 'di configurazione e pulizia. Ad esempio, prima di eseguire il test di integrazione, eseguire il seeding del database eseguendo uno script. Verifica che il tuo script abbia funzionato. Eseguire una query sul database chiamando le procedure memorizzate. Verifica che i risultati siano corretti. Pulisci il database.

L'altro mio pensiero, è avere alcune beffe che restituiscono dati validi, altre che restituiscono alcuni dati errati e che il repository locale genera un'eccezione se si verificano dati errati e verifica che l'eccezione venga generata se GetValidPOs proc restituisce dati non validi (o la finta usata nei test).

Come ho già detto, hai bisogno di un finto 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 in modo corretto. Pertanto deridi il comportamento in modo che generi eccezioni in determinati scenari. Quindi scrivi i test per assicurarti che il tuo sistema possa gestire queste eccezioni con garbo.


Questo è quello che sto cercando di fare. Ho solo difficoltà a scrivere una vera implementazione che funzionerà allo stesso modo della simulazione, poiché il nostro accesso ai dati non è favorevole all'utilizzo di un ORM. La maggior parte dei dati di cui ho bisogno si trova in diversi sistemi e dovrebbe essere accessibile tramite i servizi Web ... anche durante l'aggiornamento.
CaffGeek

0

Proprio come test di unità Java o Javascript significa scrivere test di unità utilizzando il linguaggio Java per java e test di unità funzioni Javascript con Javascript, scrivere test automatizzati per guidare l'utente a scrivere procedure memorizzate significa che la libreria di test di unità che stai cercando si basa su una memoria procedure.

Detto in altro modo, utilizzare le stored procedure per testare le stored procedure poiché:

  • poiché stai sviluppando nella lingua della procedura, dovresti avere la capacità di scrivere i test nella lingua della procedura
  • scrivere test nella tua lingua procedurale aumenterà le tue abilità nella lingua procedurale che a sua volta aiuterà lo sviluppo del tuo prodotto
  • avrai accesso diretto a tutti gli strumenti forniti dal tuo DB e potrai utilizzare anche questi strumenti per semplificare il più possibile i test unitari
  • i test unitari che sono memorizzati nello stesso DB delle procedure che stanno testando saranno veloci (la cosa più vicina al test unitario come le velocità) perché non si attraversano i confini del sistema

Proprio come TDD in una lingua OO, vuoi che il tuo test unitario configuri solo una riga o più di dati per testare ciò di cui ha bisogno per la procedura (minimalismo, ha solo ciò di cui hanno bisogno i tuoi test semplici). Il risultato è che avrai diversi test unitari per ogni procedura memorizzata. Questi semplici test saranno più facili da gestire rispetto ai test complicati che dipendono da un set di dati di grandi dimensioni che non è facilmente riconducibile a ciò di cui il test ha effettivamente bisogno.

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.