2010-04-10 9 views
14

Je suis novice en Ruby et j'ai essayé d'apprendre Rake, RSpec et Cucumber. J'ai trouvé du code qui m'aidera à tester mes tâches Rake, mais j'ai du mal à le faire fonctionner. On m'a dit ici: http://blog.codahale.com/2007/12/20/rake-vs-rspec-fight/ de laisser tomber ceci:Test d'une tâche rake dans rspec (et concombre)

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 

dans mon fichier spec_helper.rb.

J'ai réussi à prendre ce code à exécuter et à mes pas de concombre comme ceci:

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 

mais lorsque je tente de faire avancer les choses qui travaillent dans rspec, je reçois l'erreur suivante.

ArgumentError dans 'tâche Rake install_grapevine doit installer à le répertoire mygrapevine'

nombre incorrect d'arguments (1 pour 2) /spec/spec_helper.rb: 21: dans instance_eval' /spec/spec_helper.rb: 21:in bloc invoquons! ' /spec/spec_helper.rb: 20: dans each' /spec/spec_helper.rb: 20:in invoke! ' /spec/tasks/rakefile_spec.rb:12:in `bloc (2 niveaux) dans '

Malheureusement, j'ai un peu moins d'une semaine de rubis sous par la ceinture, de sorte que la substance de métaprogrammation est terminée ma tête. Quelqu'un pourrait-il me pointer dans la bonne direction?

+0

même sans RSpec: http://stackoverflow.com/questions/3530/how-do-i-rake-tasks-within-a-ruby-script –

Répondre

19

Cela fonctionne pour moi: (Rails3/Ruby 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 

Remplacez votre nom de tâche Rake ici et notez également que votre besoin peut être "lib/tâches/cron" si vous ne Ayez le dossier lib dans votre chemin de chargement.

Je suis d'accord que vous ne devriez faire qu'un minimum de travail dans la tâche Rake et pousser le reste à des modèles pour faciliter les tests. Cela étant dit, je pense qu'il est important de s'assurer que le code est réellement exécuté dans mes tâches cron lors de mes tests d'intégration, donc je pense qu'un test très léger des tâches de rake est justifié.

+3

J'ai tendance à utiliser "execute" au lieu d'invoquer lors de mes tests . Spécialement si de nombreuses étapes dépendent du test de la tâche rake, cela évite seulement d'être capable d'exécuter la tâche rake une fois. Ref: http://stackoverflow.com/questions/2532427/why-is-rake-not-able-to-invoke-multiple-tasks-consecutively –

16

Puisque tester le râteau est tout simplement trop pour moi, j'ai tendance à déplacer ce problème. Chaque fois que je me trouve avec une tâche de rake longue que je veux tester, je crée un module/classe dans lib/ et déplace tout le code de la tâche là. Cela laisse la tâche à une seule ligne de code Ruby, qui délègue à quelque chose de plus testable (classe, module, vous l'appelez). La seule chose qui reste non testée est de savoir si la tâche rake appelle la bonne ligne de code (et passe les bons paramètres), mais je pense que c'est OK.

Il peut être utile de nous dire quelle est la 21ème ligne de votre spec_helper.rb. Mais étant donné que l'approche que vous avez affichée creuse profondément dans rake (en se référant à ses variables d'instance), je l'abandonnerais entièrement pour ce que j'ai suggéré dans le paragraphe précédent.

5

Je viens de passer un peu de temps à faire du concombre pour exécuter une tâche de ratissage, alors j'ai pensé partager mon approche. Note: Ceci utilise Ruby 2.0.0 et Rake 10.0.4, mais je ne pense pas que le comportement ait changé depuis les versions précédentes.

Il y a deux parties à cela. Le premier est simple: avec une instance correctement configurée de Rake::Application, nous pouvons accéder aux tâches en appelant le #[] (par exemple rake['data:import']).Une fois que nous avons une tâche que nous pouvons exécuter en appelant #invoke et en passant dans les arguments (par exemple rake['data:import'].invoke('path/to/my/file.csv')

La deuxième partie est plus délicate:.. La mise en place correctement une instance de Rake::Application de travailler avec une fois que nous avons fait nous require 'rake' avoir accès au module Rake. Il a déjà une instance d'application, disponible à partir de Rake.application, mais il n'est pas encore configuré - il ne connaît aucune de nos tâches rake.Il sait cependant où trouver notre Rakefile, en supposant nous avons utilisé l'un des noms de fichier standard:. rakefile, Rakefile, rakefile.rb ou Rakefile.rb

Pour charger le rakefile que nous venons nee d pour appeler #load_rakefile sur l'application, mais avant que nous puissions faire cela, nous devons appeler #handle_options. L'appel à #handle_options remplit options.rakelib avec une valeur par défaut. Si options.rakelib n'est pas défini, la méthode #load_rakefile va exploser, car elle s'attend à options.rakelib être énumérable.

Voici l'aide que j'ai fini avec:

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 ce code dans un fichier dans features/support/ puis il suffit d'utiliser run_rake_task dans vos démarches, par exemple:

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

Le comportement pourrait ont changé depuis que la bonne réponse a été postée. Je rencontrais des problèmes en exécutant deux scénarios qui devaient exécuter la même tâche de rake (un seul était en cours d'exécution malgré moi en utilisant .execute au lieu de .invoke). J'ai pensé partager mon approche pour résoudre le problème (Rails 4.2.5 et Ruby 2.3.0).

J'ai étiqueté tous les scénarios qui nécessitent rake avec @rake et j'ai défini un crochet pour configurer râteau qu'une seule fois.

# 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 

(utilisant une variable globale est proposée ici: https://github.com/cucumber/cucumber/wiki/Hooks#running-a-before-hook-only-once)

Dans la définition de l'étape I simplement appelé $rake

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

Tous les commentaires sont les bienvenus.

+0

oui, c'était exactement la solution dont nous avions besoin - merci! –