Sto facendo un integration test alquanto lungo con Rspec. In due parole, ci sono vari tipi di utenti che si collegano al server per fare varie cose e l'ordine in cui lo fanno è importante. Per dare un'idea, il cliente impiega una giornata intera a fare a mano tutto il giro. Poiché lo stato del database deve mantenersi dall'inizio alla fine (non uso mock, è una scelta, ed inoltre per l'integrazione non penso abbiano senso) mi ritrovo con una describe con all'interno un unico blocco it. Funziona, ma è orribile per tante ragioni che immaginerete facilmente. La soluzione sarebbe https://github.com/LRDesign/rspec-steps che al posto della describe permette di usare un blocco "steps" con tanti "it" al suo interno: come spiega su github 'state is preserved between examples inside a "steps" block: any DB transactions will not roll back until the entire sequence has been complete.' Il guaio è che non funziona con le ultime versioni di rspec e non posso aspettare che lo sistemino, né ho tempo per studiarmi come funziona e provare a sistemarlo. Che sappiate, ci sono alternative rimanendo nel mondo RSpec (l'investimento ormai è troppo grande per cambiare)? Nel caso servisse, il client web è Capybara, scelto perché così un giorno potrei fare il test con il driver Selenium. Grazie Paolo
on 2012-07-09 19:50
on 2012-07-09 19:53
Sposta ogni step in un metodo, dentro al blocco it richiami i vari metodi. -- Simone 2012/7/9 Paolo Montrasio <paolo@paolomontrasio.com> > La soluzione sarebbe https://github.com/LRDesign/rspec-steps che > (l'investimento ormai troppo grande per cambiare)? Nel caso servisse, > Ml@lists.ruby-it.org > http://lists.ruby-it.org/mailman/listinfo/ml > -- Simone Carletti Application Developer Site & Blog: http://www.simonecarletti.com/ LinkedIn: http://linkedin.com/in/weppos Skype: weppos
on 2012-07-09 22:13
E' circa quello che sto facendo ma non è l'ideale avere un solo blocco it che gira per minuti senza feedback di avanzamento né feedback su dove capitano degli errori. Va detto che essendo una sequenza unica, il primo errore blocca sempre tutto quello che segue quindi forse la separazione in blocchi è irrilevante. C'è nessuno che si è trovato a scrivere test di questo genere? Che approccio avete seguito? Paolo Simone Carletti wrote in post #1068028: > Sposta ogni step in un metodo, dentro al blocco it richiami i vari > metodi. > > -- Simone > > > 2012/7/9 Paolo Montrasio <paolo@paolomontrasio.com> > >> La soluzione sarebbe https://github.com/LRDesign/rspec-steps che >> (l'investimento ormai troppo grande per cambiare)? Nel caso servisse, >> Ml@lists.ruby-it.org >> http://lists.ruby-it.org/mailman/listinfo/ml >> > > > > -- > Simone Carletti > Application Developer > > Site & Blog: http://www.simonecarletti.com/ > LinkedIn: http://linkedin.com/in/weppos > Skype: weppos
on 2012-07-10 09:14
Il giorno 09 luglio 2012 22:13, Paolo Montrasio <paolo@paolomontrasio.com>ha scritto: > E' circa quello che sto facendo ma non l'ideale avere un solo blocco > it che gira per minuti senza feedback di avanzamento n feedback su dove > capitano degli errori. Va detto che essendo una sequenza unica, il primo > errore blocca sempre tutto quello che segue quindi forse la separazione > in blocchi irrilevante. > > C' nessuno che si trovato a scrivere test di questo genere? Che > approccio avete seguito? > La separazione in metodi necessaria per ridurre la duplicazione fra gli step, e aumentare il riuso del codice. In pratica i metodi rappresentano la "descrizione" di cosa la tua app faccia, mentre l'implementazione dei metodi rappresenta "come" la fa, in questo modo, se cambi un comportamento, devi modificare solo 1 metodo. Ciao, Matteo
on 2012-07-10 15:19
Ok, quindi continuo con l'approccio che sto seguendo, visto che è quello
che suggerite. Mi sembrava un po' naif ma evidentemente non c'è di
meglio finché non rifunzionerà rspec-steps. Contraccambio la cortesia di
chi mi ha risposto con qualche dettaglio sull'organizzazione del codice,
e mi scusino quelli tra i lettori che li troveranno ovvi, e qualche
dritta su Capybara, forse meno ovvia.
Ho creato una directory spec/integration con dentro il singolo file con
tutto lo scenario da provare e spec/helpers con dei module da includere
nel test di integrazione. In questi module si possono definire
module Modulo
def self.included(base)
base.before(:all) do ... end
base.after(:all) do ... end
base.before(:each) do ... end
end
def metodo
...
end
end
con la semantica che immaginerete. Ho un solo test e quindi non mi
riguarda molto, ma è importante eliminare nell'after :all i record che
si creano nel before :all, altrimenti restano nel db tra un test e
l'altro. Ogni modulo cerca di raggruppare i metodi relativi ad un
modello o a un controller, per tenere il codice un po' organizzato.
Ecco le dritte su Capybara, che mi hanno fatto perdere del tempo.
1) Il server che sto testando risponde in modo diverso in base
all'HTTP_HOST nella request. L'HTTP_HOST si imposta in un before :each
così:
Capybara.current_session.driver.reset!
Capybara.default_host = "http://#{@domain}"
L'ho trovato a
http://forrst.com/posts/Testing_Subdomains_in_Capybara-g4M
2) Mi è anche capitato di dover fare delle chiamate ajax. Tramite
selenium è facile, ma volendo andare completamente headless, si fanno
così:
page.driver.post "http://#{@domain}" + model_path,
{ :key => "value" }, {:xhr => true}
resp = page.driver.response
resp.status.should == ...
resp.body.should == ...
Si potrebbe non passare tramite page.driver e chiamare direttamente
post, ma si perdono i cookie tra cui quello di sessione e non si sarebbe
più loggati. Riferimenti pro e contro questo approccio:
* http://suffix.be/blog/capybara-post-requests
* http://www.elabs.se/blog/34-capybara-and-testing-apis
* http://www.sinatrarb.com/testing.html
Paolo
on 2012-07-12 07:50
Il giorno 09 luglio 2012 22:13, Paolo Montrasio <paolo@paolomontrasio.com>ha scritto: > E' circa quello che sto facendo ma non l'ideale avere un solo blocco > it che gira per minuti senza feedback di avanzamento n feedback su dove > capitano degli errori. Va detto che essendo una sequenza unica, il primo > errore blocca sempre tutto quello che segue quindi forse la separazione > in blocchi irrilevante. > > C' nessuno che si trovato a scrivere test di questo genere? Che > approccio avete seguito? > Paolo ma a parte rspec-steps ti trovi bene con un test di integrazione che dura (tanti) minuti ? Ok che di integrazione, quindi lento per definizione. Ok che deve usare tutto, db compreso (ed usare mock al posto del db reale lo rende meno utile). Ma non riesci a velocizzarlo usando che so, un db locale in process invece del db server reale ? Tipo se usi oracle per il test usare sqlite :) Ciao, Sergio
on 2012-07-12 10:44
La risposta breve è "no, non mi trovo bene ma sono costretto dalle circostanze". Quella più articolata, scritta a più riprese mentre girano i test, è che l'applicazione era totalmente scoperta perché i test esistenti sono stati rotti dall'ultimo aggiornamento prima del mio arrivo e gli sviluppatori per qualche buona ragione (gli do credito) non avevano avuto tempo per sistemarli. All'inizio avevo dovuto lavorare in emergenza anch'io ma adesso è imperativo avere un test. Formalmente ci mette dai 4 ai 5 minuti a girare, ma tra i tempi di startup... (ecco mi ha detto ora "Finished in 4 minutes 1.81 seconds", adesso misuro il tempo reale col cronometro del telefono) ... e alle volte quelli di shutdown https://github.com/rspec/rspec-core/issues/601 ci vuole un po' di più ed il debug dei test è lento. Ma ci sono anche dei vantaggi, tipo fare qualche esercizio su duolingo mentre attendo :-) Più spesso però vado avanti a scrivere altri pezzi di test mentre gira quello attuale. Adesso ho quasi finito e dopo il rilascio in produzione dovrò scrivere anche qualche test più mirato su modelli e controller per provare in fretta la correttezza delle modifiche che faccio. ("Finished in 4 minutes 37.23 seconds" contro 5 minuti e 16 secondi reali - sorprendente quanto tempo ci voglia a scrivere un paragrafo se nel frattempo devi googlare cose... ora sistemo un po' di cose e poi riprendo la risposta) Come db sto usando mysql e per inciso sono incappato in http://stackoverflow.com/questions/5705185/switch-... passando alla gemma mysql2 Non ho idea se funzionerebbe anche con sqllite. Eventualmente c'è qualche versione in RAM di mysql? Mi verrebbe utile pure sul netbook quando alle volte lo uso per programmare in giro. ("Finished in 4 minutes 37.45 seconds" vs 5.18, l'overhead pare costante) Ho googlato un po' ed ho scoperto che MySQL ha l'engine MEMORY. In teoria basta mettere questo codice in un initializer, ma in pratica le tabelle mi vengono ancora create con InnoDB. # Based on AR 3.0.15 and http://blog.teksol.info/2008/12/12/mysqls-memory-e... module ActiveRecord module ConnectionAdapters class MysqlAdapter < AbstractAdapter def create_table(table_name, options = {}) #:nodoc: engine = case Rails.env when "test" "MEMORY" else "InnoDB" end super(table_name, options.reverse_merge(:options => "ENGINE=#{engine}")) end end end end L'autore si lamentava che il suo test era passato da 1.8 s a 1.1 s, che per lui era irrilevante, ma nel mio caso guadagnarei almeno un minuto. Indagherò, e farò anche la prova con sqlite. Paolo Sergio Berisso wrote in post #1068358: > Il giorno 09 luglio 2012 22:13, Paolo Montrasio > <paolo@paolomontrasio.com>ha scritto: > >> E' circa quello che sto facendo ma non l'ideale avere un solo blocco >> it che gira per minuti senza feedback di avanzamento n feedback su dove >> capitano degli errori. Va detto che essendo una sequenza unica, il primo >> errore blocca sempre tutto quello che segue quindi forse la separazione >> in blocchi irrilevante. >> >> C' nessuno che si trovato a scrivere test di questo genere? Che >> approccio avete seguito? >> > > Paolo ma a parte rspec-steps ti trovi bene con un test di integrazione > che > dura (tanti) minuti ? > Ok che di integrazione, quindi lento per definizione. > Ok che deve usare tutto, db compreso (ed usare mock al posto del db > reale > lo rende meno utile). > > Ma non riesci a velocizzarlo usando che so, un db locale in process > invece > del db server reale ? > Tipo se usi oracle per il test usare sqlite :) > > Ciao, > Sergio
on 2012-07-12 11:31
sqlite non funziona. Apparentemente non è supportato da qualche gemma che uso, che genera codice sql non compatibile. Quel codice per l'engine MEMORY di MySQL è ignorato se lo metto in un initializer o in un environment.rb e la ragione è banale: la classe non deve essere MysqlAdapter bensì Mysql2Adapter. Purtroppo ho scoperto due problemi: 1) per qualche ragione Rails è ancora in development mode quando si eseguono le create_table per popolare il database di test a partire da db/schema.rb tant'è che logga in development.log quei comandi (è Rails 3.0.15) 2) il workaround è impostare l'engine a MEMORY sempre (si potrebbero creare degli script per automatizzarlo in fase di test), ma poi ho "Mysql2::Error: The used table type doesn't support BLOB/TEXT columns" dato che alcuni modelli prevedono l'attachment di file. Pare una ragionevole cautela contro il riempirsi la memoria con dei file, ma sarebbe opportuno avere lo switch "so-quello-che-sto-facendo" perché in test non correrei di questi rischi. Conclusione, niente database in memory per questi test. Devono proprio andare sul disco. Paolo
on 2012-07-12 11:41
Il giorno 12 luglio 2012 11:31, Paolo Montrasio <paolo@paolomontrasio.com>ha scritto: > Purtroppo ho scoperto due problemi: > 2) il workaround impostare l'engine a MEMORY sempre (si potrebbero > creare degli script per automatizzarlo in fase di test), ma poi ho > "Mysql2::Error: The used table type doesn't support BLOB/TEXT columns" > dato che alcuni modelli prevedono l'attachment di file. > Sostituire con uno script nel db/schema.rb data type BLOB/TEXT con data type base (char, varchar) compatibile con MEMORY engine ? Salta il data model ? Ciao, Sergio
on 2012-07-12 12:11
Sergio Berisso wrote in post #1068405: > Il giorno 12 luglio 2012 11:31, Paolo Montrasio > <paolo@paolomontrasio.com>ha scritto: > >> Purtroppo ho scoperto due problemi: >> 2) il workaround impostare l'engine a MEMORY sempre (si potrebbero >> creare degli script per automatizzarlo in fase di test), ma poi ho >> "Mysql2::Error: The used table type doesn't support BLOB/TEXT columns" >> dato che alcuni modelli prevedono l'attachment di file. >> > > Sostituire con uno script nel db/schema.rb data type BLOB/TEXT con data > type base (char, varchar) compatibile con MEMORY engine ? > Salta il data model ? Dovrei indagare. Di sicuro prima ho scritto una sciocchezza perché dei file memorizzo solo il path in dei varchar (ma mi viene in mente che un nome di file lungo potrebbe sforare...). Ho però dei campi text che potrebbero davvero dover contenere dei testi lunghi, quindi non si può fare così. Però ho due buone notizie. Una è che ho finito il primo test di integrazione completo "Finished in 6 minutes 16.88 seconds" e la seconda è che ho trovato un'altra soluzione: http://www.bigdbahead.com/?p=183 Bisogna però installare una versione diversa di mysql, su linux probabilmente con $ sudo apt-get install mysql-cluster-server-5.1 e lo proverò un giorno in una virtual machine. Documentazione a http://www.intertad.info/server/mysql-5.1/mysql-cluster.html Paolo
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.