Vantaggi e svantaggi nel rendere comune il codice di ponteggio del test unitario

8

Per il progetto a cui io e il mio team stiamo lavorando, spesso troviamo che abbiamo bisogno di grandi pezzi di codice per gli scaffolding. La creazione di oggetti di dominio con valori corretti, l'impostazione di mock per repository, gestione della cache, ... sono tutte cose che si verificano comunemente durante i test. Un sacco di volte lavoriamo con gli stessi oggetti di base che sono al centro del nostro dominio (persona, ...) quindi molti test creano istanze di questi oggetti su cui lavorare su altri oggetti. Abbiamo molte soluzioni diverse che utilizzano il dominio di base, quindi questo tipo di codice è spesso distribuito su tali soluzioni.

Ho pensato di creare classi comuni che realizzano molte di queste impalcature. Questo ci consentirebbe di richiedere una persona completamente istanziata con tutto configurato (accesso tramite repository, memorizzazione nella cache ...). Questo rimuove il codice duplicato dai nostri singoli test di unità, ma significherebbe anche che c'è una grande quantità di codice che probabilmente fa 'troppo' per test (in quanto creerebbe un oggetto completo e non solo le parti richieste).

Qualcuno l'ha mai fatto? Ci sono intuizioni, osservazioni, pensieri ... che puoi offrire che potrebbero confermare o invalidare questo approccio?

    
posta JDT 22.12.2011 - 13:43
fonte

8 risposte

0

Tutte le possibilità riassunte nelle risposte:

Usa lo schema Builder (Bedwyr Humphreys, DXM e Pascal Mestdach)

I vantaggi:

  • Nessuna duplicazione del codice nei test
  • Può combinare diversi builder per test complessi
  • Pulisci interfaccia
  • I grafici a oggetti grandi dei costruttori possono influenzare il risultato del test

Svantaggi:

  • I builder devono essere scritti e mantenuti
  • Il codice del builder deve essere testato anche
  • Il codice del builder richiede una cura e un'attenzione costanti

Utilizza un database di test (Tony Hopkinson e Sam)

I vantaggi:

  • Semplicità
  • Tutti i dati in un unico punto centrale
  • Può utilizzare il codice di accesso ai dati già sviluppato
  • Utilizzabile per oggetti di dominio che non sono POCO (ad esempio, Active Record, ...)

Svantaggi:

  • Richiede l'uso del DAL che potrebbe interferire con i test
  • Non "puro" TDD
  • 'Black box', è necessario dare un'occhiata al database per vedere quali dati contengono gli oggetti

Crea oggetti di dominio per test (Peter)

I vantaggi:

  • Codice di test isolato
  • Nessuna "scatola nera", ciò che vedi nel codice di prova è ciò che ottieni

Svantaggi:

  • Duplicazione del codice per oggetti comuni

Utilizza facciate per oggetti di dominio (Raedwald)

I vantaggi:

  • Test semplici

Svantaggi:

  • Non utilizzabile in tutti i casi
  • Codice nascosto dietro a Facade anche i test
risposta data 29.12.2011 - 15:49
fonte
4
Creating domain objects with correct values

Uso il modello di builder per creare oggetti di dominio per i test come descritto in " Software orientato agli oggetti in crescita " libro. I builder hanno metodi statici che generano valori predefiniti per oggetti comuni con metodi per sovrascrivere i valori predefiniti.

User user = UserBuilder.anAdminUser().withEmail("[email protected]").build();

Puoi combinarli per generare oggetti più complessi.

Usiamo anche l'ereditarietà delle classi del caso di test con i simulatori comunemente usati per specifici tipi di test unitari - ad es. i test del controller mvc richiedono spesso gli stessi mock / stub per request & oggetti di risposta.

    
risposta data 22.12.2011 - 15:32
fonte
2

L'impostazione di tutti i test può essere una buona idea, in particolare se si esegue una distribuzione. Invece di usare i mock, creiamo, distribuiamo e popoliamo un database di test e quindi puntiamo il test sull'istanza. Non esattamente come dovresti farlo come da manuale, ma funziona, ancora di più per noi perché usiamo il nostro codice di implementazione per farlo.

    
risposta data 22.12.2011 - 14:19
fonte
2

Bedwyr Humphreys sta parlando di una soluzione Builder che ho usato un paio di volte, e funziona davvero bene. Quasi nessuna duplicazione di codice e la costruzione di oggetti di dominio in modo pulito e pulito.

User user = UserBuilder.anAdminUser().withEmail("[email protected]").build();

Non sono un fan della generazione di codice (scaffolding, codemith, lblgen, ..). Ti aiuta solo a scrivere codice duplicato molto più velocemente, non proprio per la manutenzione. Se un framework sta causando la scrittura di molti codici duplicati (test), forse è necessario trovare un modo per spostare il codice base dal framework.

Se continui a scrivere il codice di accesso al repository, potresti essere in grado di risolverlo con un repository di base generico e di avere una base di test generica da cui ereditare i test delle tue unità.

Per evitare la duplicazione del codice AOP può anche aiutare per cose come la registrazione, la memorizzazione nella cache, ...

    
risposta data 23.12.2011 - 10:17
fonte
1

Potresti voler riconsiderare il design del codice che stai testando. Forse ha bisogno di alcune istanze del modello di progettazione della facciata? Il tuo codice di prova potrebbe utilizzare le facciate e sarebbe più semplice.

    
risposta data 22.12.2011 - 15:28
fonte
1

Posso riguardare questa domanda. Nel mio team abbiamo già provato a fare TDD ed era la stessa storia, troppi test richiedevano molti metodi di scaffolding / utility che semplicemente non avevamo. A causa delle pressioni temporali e di poche altre decisioni non così giuste, siamo finiti con test unitari molto lunghi e reinventati. Nel corso di una release, quei test unitari sono diventati così complicati e difficili da mantenere, abbiamo dovuto eliminarli.

Al momento sto lavorando con pochi altri sviluppatori per reintrodurre TDD nel team, ma invece di dire semplicemente alle persone, fare test di scrittura, questa volta vogliamo farlo con attenzione e le persone hanno le giuste competenze e gli strumenti giusti. L'assenza del codice di supporto comune è una cosa che abbiamo identificato dove dobbiamo migliorare in modo che quando gli altri iniziano a scrivere test, tutto il codice che scrivono sia piccolo e diretto al punto.

Non ho ancora tanta esperienza in questo settore come vorrei, ma poche cose che suggerirei in base al lavoro che abbiamo svolto finora e alcuni compiti che ho svolto:

  1. Il codice comune è una buona cosa. Fare lo stesso lavoro 5 volte non ha senso e sotto la pressione del tempo almeno 4 di queste volte saranno semi-assed.
  2. Il mio piano è di creare un quadro comune, quindi creare un gruppo dirigente TDD composto da 4-6 sviluppatori. Quindi scrivere test unitari, imparare il framework e fornire feedback su di esso. Poi fai andare questi ragazzi al resto del gruppo e guida gli altri nella produzione di test unitari mantenibili che utilizzino la potenza del framework. Inoltre, questi ragazzi raccolgono feedback e lo riportano nel framework.
  3. Per gli esempi che hai fornito con la classe Person, sto attualmente esaminando l'uso di mockpp o googlemock framework. Per ora, mi sono appoggiato a googlemock, ma non ho ancora provato. In entrambi i casi, perché scrivere qualcosa che può essere "preso in prestito"
  4. Fai attenzione alla creazione di proiettori "dio" che istanziano gli oggetti per tutti i test, indipendentemente dal fatto che quei test abbiano effettivamente bisogno di quegli oggetti. Questo è ciò che xUnit Test Patterns chiama "General Fixture Pattern". Ciò potrebbe causare alcuni problemi come: a) ogni test impiega troppo tempo a funzionare a causa dell'eccessivo codice di configurazione eb) i test finiscono per avere troppe dipendenze non necessarie. Nel nostro framework, ogni classe di test sceglie esattamente le funzionalità di framework di cui ha bisogno per essere eseguita, quindi non è incluso alcun bagaglio non necessario (in futuro potremmo cambiarlo in ogni metodo scelto in modo indipendente). Ho tratto ispirazione da Boost e Modern C ++ Design e ho trovato una classe template base che prende una mpl :: vettore delle funzionalità necessarie e crea una gerarchia di classi personalizzata specifica per quel test. Alcuni veramente nifty capitano cruch decoder ring super crazy black magic template stuff:)
risposta data 23.12.2011 - 05:21
fonte
1

"Qualsiasi problema di informatica può essere risolto con un ulteriore livello di riferimento indiretto, ma di solito questo crea un altro problema." - David Wheeler

(disclaimer: faccio parte del team di JDT, quindi ho una conoscenza più approfondita del problema presentato qui)

Il problema essenziale qui è che gli oggetti nel framework si aspettano una connessione al database, in quanto liberamente basato sul framework CSLA di Rockford Lhotka. Questo rende i mocking quasi impossibili (come dovresti cambiare il framework per supportarlo) e viola anche il principio della scatola nera che dovrebbe avere un test unitario.

La tua soluzione proposta, sebbene non sia una cattiva idea in sé, non richiederà solo MOLTO lavoro per arrivare a una soluzione stabile, ma aggiungerà anche un altro livello di classi che deve essere mantenuto ed esteso, aggiungendo ancora più complessità a una situazione già complessa.

Anche se sono d'accordo sul fatto che dovresti sempre cercare la soluzione migliore, in una situazione del mondo reale come questa, anche l'essere pratico ha i suoi valori.

E la praticità suggerisce la seguente opzione:

Utilizza un database.

Sì, so che tecnicamente parlando non è più un test di unità "reale", ma parlando nel momento esatto in cui attraversi un limite di classe nel tuo test, non è nemmeno più un vero test di unità. Quello che dobbiamo chiederci qui è, vogliamo test di unità "puri" che richiedono carichi di ponteggio e codice di colla, o vogliamo codice testabile ?

Potresti anche sfruttare il fatto che il framework utilizzato incapsula tutti gli accessi al database per te ed esegui i test su un database sandbox pulito. Se poi esegui ciascun test nella sua stessa transazione e esegui il rollback alla fine di ciascun set di test "logico", non dovresti avere problemi di interdipendenza o ordine di test.

Non tentare di estrarre un database da un database.

    
risposta data 23.12.2011 - 07:31
fonte
0

Vorrei avvertire di avere troppo codice che fa troppa "magia nera" senza che gli sviluppatori sappiano cosa si sta configurando esattamente. Inoltre, col tempo, diventerai molto dipendente da questo codice / dati. Ecco perché non sono entusiasta di basare i test sui database di test. Credo che i database di test siano destinati al test come utente, non ai test automatizzati.

Detto questo, il tuo problema è reale. Farei alcuni metodi comuni per impostare i dati che ti servono regolarmente. Idealmente, prova a parametrizzare i metodi, perché alcuni test richiedono dati diversi.

Ma eviterei di creare grafici di oggetti grandi. È meglio che i test vengano eseguiti con il minimo di dati di cui hanno bisogno. Se ci sono molti altri dati, potresti inaspettatamente influenzare i risultati del test (scenario peggiore).

Mi piace mantenere i miei test "silo" il più possibile. È una domanda soggettiva però. Ma sceglierei i metodi più comuni con i parametri in modo da poter impostare i dati del test solo con poche righe di codice.

    
risposta data 22.12.2011 - 15:20
fonte

Leggi altre domande sui tag