Тестирование граблей в rspec (и огурце) - PullRequest
14 голосов
/ 10 апреля 2010

Я новичок в Ruby и пытаюсь выучить Rake, RSpec и Cucumber. Я нашел некоторый код, который поможет мне протестировать мои Rake-задачи, но у меня возникли проблемы с его работой. Мне сказали здесь: http://blog.codahale.com/2007/12/20/rake-vs-rspec-fight/ бросить это:

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

в мой файл spec_helper.rb.

Мне удалось вынуть этот код и запустить его в моих шагах от огурца, как это:

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

но когда я пытаюсь заставить все работать в rspec, я получаю следующую ошибку.

ArgumentError в задаче 'Rake install_grapevine должен установить в каталог mygrapevine '

неверное количество аргументов (1 для 2) /spec/spec_helper.rb: 21: в instance_eval' /spec/spec_helper.rb: 21:in блоке в invoke! ' /spec/spec_helper.rb: 20: in each' /spec/spec_helper.rb: 20:in invoke! ' /spec/tasks/rakefile_spec.rb:12:in `блок (2 уровня) в «

К сожалению, у меня чуть меньше недели рубина под поясом, так что метапрограммирование над моей головой. Может ли кто-нибудь указать мне правильное направление?

Ответы [ 4 ]

19 голосов
/ 30 ноября 2010

Это работает для меня: (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

Замените здесь имя вашей задачи rake, а также обратите внимание, что ваш запрос может быть "lib / tasks / cron", если в вашем пути загрузки нет папки lib.

Я согласен с тем, что вы должны выполнять только минимальную работу в задаче Rake и передавать остальные модели для облегчения тестирования. При этом я считаю, что важно убедиться, что код фактически выполняется в моих задачах cron во время интеграционных тестов, поэтому я считаю, что очень мягкое тестирование rake-задач оправдано.

16 голосов
/ 17 мая 2010

Поскольку тестирование рейка - это слишком много для меня, я склонен решать эту проблему. Всякий раз, когда я сталкиваюсь с долгой задачей с граблями, которую я хочу протестировать, я создаю модуль / класс в lib/ и перемещаю туда весь код из задачи. Это оставляет задачу в одной строке кода Ruby, которая делегирует что-то более тестируемое (класс, модуль, назовите его). Единственное, что остается непроверенным, это то, что задача rake вызывает правильную строку кода (и передает правильные параметры), но я думаю, что все в порядке.

Может быть полезно сообщить нам, какая 21-я строка вашего spec_helper.rb. Но, учитывая тот факт, что вы опубликовали свой подход, вглубь рейка (ссылаясь на переменные экземпляра), я бы полностью отказался от него из-за того, что я предложил в предыдущем абзаце.

5 голосов
/ 14 декабря 2013

Я потратил немного времени на то, чтобы получить огурец для выполнения граблей, поэтому решил поделиться своим подходом. Примечание: это использует Ruby 2.0.0 и Rake 10.0.4, но я не думаю, что поведение изменилось с предыдущих версий.

Это две части. Первый прост: с правильно настроенным экземпляром Rake::Application, тогда мы можем получить доступ к задачам по нему, вызвав #[] (например, rake['data:import']). Когда у нас есть задача, мы можем запустить ее, вызвав #invoke и передав аргументы (например, rake['data:import'].invoke('path/to/my/file.csv').

Вторая часть более неловкая: правильно настроить экземпляр Rake::Application для работы. После того, как мы сделали require 'rake', у нас есть доступ к модулю Rake. У него уже есть экземпляр приложения, доступный с Rake.application, но он еще не настроен - он не знает ни о одной из наших задач по рейку. Однако он знает, где найти наш Rakefile, при условии, что мы использовали одно из стандартных имен файлов: rakefile, Rakefile, rakefile.rb или Rakefile.rb.

Чтобы загрузить rakefile, нам просто нужно вызвать #load_rakefile в приложении, но прежде чем мы сможем это сделать, нам нужно вызвать #handle_options. Вызов #handle_options заполняет options.rakelib значением по умолчанию. Если options.rakelib не установлено, то метод #load_rakefile будет взорван, поскольку он ожидает, что options.rakelib будет перечислимым.

Вот помощник, с которым я закончил:

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)

Вставьте этот код в файл в features/support/, а затем просто используйте run_rake_task в своих действиях, например:

When /^I import data from a CSV$/ do
  run_rake_task 'data:import', 'path/to/my/file.csv'
end
3 голосов
/ 02 марта 2016

Поведение могло измениться после публикации правильного ответа. У меня возникли проблемы при выполнении двух сценариев, для которых требовалось выполнить одну и ту же задачу rake (выполнялся только один, несмотря на то что я использовал .execute вместо .invoke). Я решил поделиться своим подходом к решению проблемы (Rails 4.2.5 и Ruby 2.3.0).

Я пометил все сценарии, которые требуют рейка, с помощью @rake, и я определил ловушку для установки рейка только один раз.

# 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

(здесь предлагается использование глобальной переменной: https://github.com/cucumber/cucumber/wiki/Hooks#running-a-before-hook-only-once)

В определении шага я просто назвал $rake

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

Любые отзывы приветствуются.

...