Sembra che ci siano due approcci totalmente diversi al test, e vorrei citarli entrambi.Test: come concentrarsi sul comportamento anziché sull'implementazione senza perdere velocità?
Il fatto è che queste opinioni sono state pronunciate 5 anni fa (2007), e sono interessato, cosa è cambiato da allora e da che parte dovrei andare.
La teoria è che i test dovrebbero essere agnostica del attuazione. Questo porta a test meno fragili e in realtà prova l'esito (o il comportamento).
Con RSpec, mi sento come l'approccio comune di scherno completamente tuoi modelli per testare i controller finisce per costringere a guardare troppo nell'attuazione del controller.
Questo di per sé non è un problema, ma il problema è che anche esso identifica lo molto nel controller per dettare come viene utilizzato il modello. Perché è importante il se il mio controller chiama Thing.new? Cosa succede se il mio controller decide di di prendere Thing.create! e la via di salvataggio? Cosa succede se il mio modello ha un metodo di inizializzazione speciale , come Thing.build_with_foo? Le mie specifiche per il comportamento di non dovrebbero fallire se cambio l'implementazione.
Questo problema peggiora ulteriormente quando si hanno risorse nidificate e sono creazione di più modelli per controller. Alcuni dei miei metodi di installazione terminano con lo fino a 15 o più righe e MOLTO fragile.
l'intenzione di RSpec è quello di isolare completamente la logica di controllo da tuoi modelli, che suona bene in teoria, ma quasi corre contro il grano per uno stack integrato come Rails. Soprattutto se si pratica lo disciplina del controller magro/grasso, la quantità di logica nel controller diventa molto piccola e l'installazione diventa enorme.
Che cosa significa fare un aspirante BDD? Facendo un passo indietro, il comportamento che I vuole veramente testare non è che il mio controller chiama Thing.new, ma che dati i parametri X, crea una nuova cosa e reindirizza ad esso.
David Chelimsky:
E 'tutta una questione di compromessi.
Il fatto che AR scelga l'ereditarietà piuttosto che la delega ci mette in un bind di test - dobbiamo essere accoppiati al database O dobbiamo essere più intimi con l'implementazione. Accettiamo questa scelta di progettazione perché sfruttiamo i vantaggi espressivi e DRY-ness.
In lotta con il dilemma, ho scelto test più rapidi al costo di leggermente più fragile. Stai scegliendo test meno fragili al costo di di quelli che funzionano leggermente più lentamente. È un compromesso in entrambi i casi.
In pratica, ho eseguito il test centinaia, se non migliaia, di volte al giorno (io uso autotest e adottare misure molto granulari) e mi cambiano se io uso “nuovi” o “Crea” quasi mai. Anche a causa di passaggi granulari, i nuovi modelli che appaiono sono piuttosto volatili all'inizio. L'approccio valid_thing_attrs minimizza il dolore da questo un po ', ma significa comunque che ogni nuovo campo obbligatorio significa che devo cambiare valid_thing_attrs.
Ma se il tuo approccio funziona in pratica per te, allora va bene! Nel caso , ti consiglio vivamente di pubblicare un plug-in con i generatori che producono gli esempi nel modo che preferisci. Sono sicuro che un sacco di persone che beneficeranno di questo persone.
Per curiosità, come spesso si usano dei mock nei test/spec? Forse sto facendo qualcosa di sbagliato, ma lo trovo severamente limitato a . Dal passaggio a rSpec più di un mese fa, ho fatto ciò che raccomandano nei documenti in cui il controller e i livelli di vista non colpiscono affatto il database ei modelli sono completamente derisi out. Questo ti dà un buon aumento di velocità e rende alcune cose più facili, ma sto trovando i contro di fare questo superano di gran lunga i professionisti. Dal usando i mock, le mie specifiche sono diventate un incubo di manutenzione. Le specifiche hanno lo scopo di testare il comportamento, non l'implementazione. Non mi interessa se è stato chiamato un metodo, voglio solo assicurarmi che l'uscita risultante sia corretta. Poiché il mocking rende le specifiche schizzinose sull'implementazione dello , rende semplici i refactoring (che non cambiano il comportamento dello ) senza dover tornare costantemente indietro e "correggere" le specifiche. Sono molto supponente su cosa spec/test dovrebbero coprire . Un test dovrebbe interrompersi solo quando l'app si interrompe. Questo è uno dei motivi per cui faccio fatica a testare il livello vista perché lo trovo troppo rigido. Spesso si verifica un'interruzione dei test senza che l'app si interrompa quando modifica le piccole cose nella vista. Sto riscontrando lo stesso problema con le mezze . In cima a tutto questo, ho appena realizzato oggi che il tintinnio/stubing un metodo di classe (a volte) si aggira tra le specifiche. Le specifiche dovrebbero essere autonomo e non influenzato da altre specifiche. Questo rompe la regola e porta a bug insidiosi. Che cosa ho imparato da tutto questo? Be attento dove si usa il mocking. Lo stubing non è così male, ma ha ancora alcuni degli stessi problemi.
Ho impiegato le ultime ore e ho rimosso quasi tutti i mock dalle mie specifiche. Ho anche unito il controller e ho visto le specifiche in uno usando "integrated_views" nelle specifiche del controller. Sto anche caricando tutti i dispositivi per ogni specifica del controller, quindi ci sono alcuni dati di test per riempire le viste con . Il risultato finale? Le mie specifiche sono più brevi, più semplici, più consistenti, meno rigide, e testano l'intero stack insieme (modello, vista, controller) in modo che nessun bug possa scivolare attraverso le fessure. Sono non dicendo che questo è il modo "giusto" per tutti. Se il tuo progetto richiede un caso specifico molto rigido, potrebbe non essere adatto a te, ma nel mio caso questo è un mondo migliore di quello che avevo prima di usare i mock.Sono ancora pensare che lo stub è una buona soluzione in alcuni punti, quindi sto ancora facendo .
È una buona domanda; Ho visto un sacco di test di unità fragili a causa di tutte le beffa che sta succedendo.I test delle unità JavaScript possono essere peggiori. –
Uomo, mi piacciono tutti questi pensieri! Il problema più grande qui è probabilmente Rails. In GOOS, dicono che non dovresti simulare il codice di terze parti (che includerebbe ActiveRecord), perché non puoi apportare modifiche al design che lo riguardano. Questo è essenzialmente il problema, David sottolinea che preferirebbe usare la composizione, ma non può b/c AR scegliere l'ereditarietà (se vuole fare le cose come tradizionalmente fatto in Rails). Personalmente, ho molte idee e non so quali siano giuste. Bella domanda, però. –