Напишите rspec для задачи rake

у меня задача /lib/crawler.rake такая:

namespace :crawler do
  area_names = Dir[Rails.root.join("lib", "crawler", "*.rb")].map do |file_name|
    File.basename(file_name, ".rb")
  end

  area_names.each do |area_name|
    task area_name.to_sym => :environment do
      logger = Logger.new("log/crawl_#{area_name}.log")

      # do something

      parallel_results = crawler.crawl
      mutex = Mutex.new

      Parallel.each(parallel_results, in_threads: [parallel_results.count, CRAWL_CONFIG["building_thread_max"]].min) do |pages|
        begin
          # do something
        rescue => e
          # do something
          raise e
        end
      end

      Availability.update_by_grounds_and_time
    end
  end
end

Здесь логика, если после распараллеливания все в порядке, мы вызовем метод update_by_grounds_and_time для обновления Availability; если получим ошибку, мы прекратим действие и поднимем ошибку.

Итак, я хочу написать rspec для проверки этих случаев, я хочу имитировать/заглушить вывод задачи здесь (пропустить или поднять ошибку) и проверить, вызывали ли мы метод update_by_grounds_and_time? Может нам не нужно вызывать действительно задачу? мы можем использовать Rspec Mock?

Можешь мне помочь! Благодарить


person Tai Tri Vo    schedule 14.06.2016    source источник


Ответы (2)


Если он определен в Rakefile, попробуйте следующее:

require 'rake'

RSpec.describe "Rake Tasks" do
  before do
    file, path = Rake.application.find_rakefile_location
    Rake.load_rakefile("#{path}/#{file}")
  end

  it "should invoke some tasks" do
    expect(Availability).to receive(:update_by_grounds_and_time)
    Rake.application["crawler:#{area_name}"].invoke
  end
end

Если он определен в foo.rake, попробуйте этот:

require 'rake'

RSpec.describe "Rake Tasks" do
  before do
    Rake.application.rake_require('/path/to/lib/tasks/foo')
  end

  it "should invoke some tasks" do
    expect(Availability).to receive(:update_by_grounds_and_time)
    Rake.application["crawler:#{area_name}"].invoke
  end
end

ОБНОВЛЕНИЕ (случай ошибки)

Например

# foo.rake
Parallel.each(parallel_results, in_threads: [parallel_results.count, CRAWL_CONFIG["building_thread_max"]].min) do |pages|
  begin
    foo = Foo.new
    foo.bar
    # do something else
  rescue => e
    # do something
    raise e
  end
end

# foo_spec.rb
require 'rake'

RSpec.describe "Rake Tasks" do
  before do
    Rake.application.rake_require('/path/to/lib/tasks/foo')
  end

  it "should not call Availability#update_by_grounds_and_time if error raised" do
    allow_any_instance_of(Foo).to receive(:bar).and_raise(StandardError)
    expect(Availability).to_not receive(:update_by_grounds_and_time)
    expect { Rake.application["crawler:#{area_name}"].invoke }.to raise_error(StandardError)
  end
end
person scorix    schedule 14.06.2016
comment
Спасибо @scorix. Но могу ли я не вызывать настоящую задачу? если мы поднимем ошибку (перейдем к блоку rescue), мы остановим действие и не запустим строку Availability.update_by_grounds_and_time. Итак, я хочу проверить здесь 2 случая: если прошло => я вызвал update_by_grounds_and_time после запуска задачи, если возникла ошибка => я не звонил - person Tai Tri Vo; 15.06.2016
comment
когда я пытаюсь запустить с ошибкой, rpsec возвращает ошибку, например: Failure/Error: Rake.application["crawler:#{area_name}"].invoke StandardError: StandardError? мы можем пройти это дело? - person Tai Tri Vo; 16.06.2016
comment
Извините, это моя ошибка. Обновлено. - person scorix; 16.06.2016
comment
ах, мне нужно определить задачу Rake::Task.define_task(:environment) в before блоке? - person Tai Tri Vo; 16.06.2016

Что я обычно делаю в таких случаях, так это извлекаю мясо в отдельный класс/служебный объект/что угодно, что гораздо проще тестировать. Затем задача rake становится просто инициатором этого объекта и, как таковая, не нуждается в тестировании.

person Sergio Tulentsev    schedule 15.06.2016