2010-04-10 6 views
14

Sono nuovo a Ruby e ho cercato di imparare Rake, RSpec e Cucumber. Ho trovato un codice che mi aiuterà a testare le mie attività di Rake, ma ho difficoltà a farlo funzionare. Mi è stato detto qui: http://blog.codahale.com/2007/12/20/rake-vs-rspec-fight/ far cadere questo:Test di un'attività di rake in rspec (e cetriolo)

def describe_rake_task(task_name, filename, &block) 
    require "rake" 

    describe "Rake task #{task_name}" do 
    attr_reader :task 

    before(:all) do 
     @rake = Rake::Application.new 
     Rake.application = @rake 
     load filename 
     @task = Rake::Task[task_name] 
    end 

    after(:all) do 
     Rake.application = nil 
    end 

    def invoke! 
     for action in task.instance_eval { @actions } 
     instance_eval(&action) 
     end 
    end 

    instance_eval(&block) 
    end 
end 

nel mio file spec_helper.rb.

sono riuscito a prendere questo codice fuori e correre nei miei passi cetriolo in questo modo:

When /^I run the update_installers task$/ do 
@rake = Rake::Application.new 
Rake.application = @rake 
load "lib/tasks/rakefile.rb" 
@task = Rake::Task["update_installers"] 

for action in @task.instance_eval { @actions } 
    instance_eval(&action) 
end 

instance_eval(&block) 

Rake.application = nil 
end 

ma quando provo a far funzionare le cose in RSpec, ottengo l'errore seguente.

ArgumentError in 'Rake compito install_grapevine dovrebbe installare al directory mygrapevine'

numero errato di argomenti (1 per 2) /spec/spec_helper.rb: 21: in instance_eval' /spec/spec_helper.rb: 21:in blocco nel invoke! ' /spec/helec_helper.rb: 20: in each' /spec/spec_helper.rb: 20:in invoke! ' /spec/tasks/rakefile_spec.rb:12:in `blocco (2 livelli) in '

Purtroppo, ho poco meno di una settimana di rubino sotto da cintura, in modo la roba metaprogrammazione è finita la mia testa. Qualcuno potrebbe indicarmi la giusta direzione?

+0

stesso senza RSpec: http://stackoverflow.com/questions/3530/how-do-i-rake-tasks-within-a-ruby-script –

risposta

19

questo funziona per me: (Rails3/Rubino 1.9.2)

When /^the system does it's automated tasks$/ do  
    require "rake" 
    @rake = Rake::Application.new 
    Rake.application = @rake 
    Rake.application.rake_require "tasks/cron" 
    Rake::Task.define_task(:environment) 
    @rake['cron'].invoke 
end 

sostituire il nome del task rake qui e si noti anche che il vostro bisogno può essere "lib/task/cron" se non lo fai avere la cartella lib nel percorso di caricamento.

Sono d'accordo sul fatto che si dovrebbe svolgere solo un lavoro minimo nell'attività Rake e spingere il resto sui modelli per facilità di test. Detto questo, penso che sia importante assicurarsi che il codice venga eseguito REALMENTE nei miei compiti cron durante i miei test di integrazione, quindi penso che sia molto giustificato un test mite delle attività di rake.

+3

Tendo ad usare "execute" invece di invocare durante i miei test . Specialmente se molti passaggi dipendono dal test del rake, questo evita solo di essere in grado di eseguire l'attività di rake una sola volta. Rif: http://stackoverflow.com/questions/2532427/why-is-rake-not-able-to-invoke-multiple-tasks-consecutively –

16

Poiché testare il rake è troppo per me, tendo a spostare questo problema. Ogni volta che mi ritrovo con un compito di rake lungo che voglio testare, creo un modulo/classe in lib/ e trasferisco tutto il codice dall'attività lì. Questo lascia il compito a una singola riga di codice Ruby, che delega a qualcosa di più testabile (classe, modulo, tu lo chiami). L'unica cosa che non viene testata è se l'attività rake richiama la giusta riga di codice (e passa i parametri giusti), ma penso che sia OK.

Potrebbe essere utile dirci quale è la 21 ° linea del tuo . Ma dato che l'approccio che hai postato scava in profondità nel rake (riferendosi alle sue variabili di istanza), lo abbandonerei del tutto per quello che ho suggerito nel paragrafo precedente.

5

Ho appena trascorso un po 'di tempo con il cetriolo per eseguire un'attività di rake, quindi ho pensato di condividere il mio approccio. Nota: questo utilizza Ruby 2.0.0 e Rake 10.0.4, ma non penso che il comportamento sia cambiato rispetto alle versioni precedenti.

Ci sono due parti a questo. Il primo è semplice: con un'istanza correttamente impostata di Rake::Application, possiamo accedere alle attività su di esso chiamando #[] (ad esempio rake['data:import']).Una volta che abbiamo un compito siamo in grado di eseguirlo chiamando #invoke e passando gli argomenti (ad esempio rake['data:import'].invoke('path/to/my/file.csv')

La seconda parte è più imbarazzante:.. Impostare correttamente un'istanza di Rake::Application di lavorare con una volta che abbiamo fatto noi require 'rake' avere accesso al modulo Rake. Ha già un'istanza dell'applicazione, disponibile da Rake.application, ma non è ancora impostata, non conosce alcuna delle nostre attività di rake, ma sa dove trovare il nostro Rakefile, assumendo abbiamo utilizzato uno dei nomi di file standard:. rakefile, Rakefile, rakefile.rb o Rakefile.rb

Per caricare la rakefile abbiamo appena nee d per chiamare #load_rakefile sull'applicazione, ma prima che possiamo farlo è necessario chiamare #handle_options. La chiamata a #handle_options popola options.rakelib con un valore predefinito. Se options.rakelib non è impostato, il metodo #load_rakefile esploderà, in quanto prevede che sia enumerabile lo options.rakelib.

Ecco l'aiutante che ho finito con:

module RakeHelper 
    def run_rake_task(task_name, *args) 
    rake_application[task_name].invoke(*args) 
    end 

    def rake_application 
    require 'rake' 
    @rake_application ||= Rake.application.tap do |app| 
     app.handle_options 
     app.load_rakefile 
    end 
    end 
end 

World(RakeHelper) 

Pop che il codice in un file in features/support/ e poi basta usare run_rake_task nei vostri passi, ad esempio:

When /^I import data from a CSV$/ do 
    run_rake_task 'data:import', 'path/to/my/file.csv' 
end 
3

Il comportamento potrebbe sono cambiati da quando è stata pubblicata la risposta corretta. Ho riscontrato problemi nell'esecuzione di due scenari che dovevano eseguire lo stesso rake task (solo uno veniva eseguito nonostante io abbia utilizzato .execute anziché .invoke). Ho pensato di condividere il mio approccio per risolvere il problema (Rails 4.2.5 e Ruby 2.3.0).

Ho taggato tutti gli scenari che richiedono il rake con @rake e ho definito un hook per impostare il rake una sola volta.

# hooks.rb 
Before('@rake') do |scenario| 
    unless $rake 
    require 'rake' 
    Rake.application.rake_require "tasks/daily_digest" 
    # and require other tasks 
    Rake::Task.define_task(:environment) 
    $rake = Rake::Task 
    end 
end 

(Utilizzo di una variabile globale viene suggerito qui: https://github.com/cucumber/cucumber/wiki/Hooks#running-a-before-hook-only-once)

Nella definizione fase ho semplicemente chiamato $rake

# step definition 
Then(/^the daily digest task is run$/) do 
    $rake['collector:daily_digest'].execute 
end 

Qualsiasi commento è benvenuto.

+0

sì, quella era esattamente la correzione di cui avevamo bisogno - grazie! –