Как вы интегрируете функции Resque в огурец? - PullRequest
4 голосов
/ 21 июня 2011

Я пытался применить метод Квадрата по включению Resque в свои интеграционные тесты без особой удачи.Я не уверен, что Resque и / или Cucumber сильно изменились с августа 2010 года.

Ниже вы найдете подход, который я использовал, и, возможно, вы можете либо:

  1. Скажите, где я ошибся и как я могу это исправить
  2. Рекомендовать совершенно новый способ интеграции Resque в функции Cucumber

Что я сделал, чтобы установить его

В блоге Square не было четких шагов по его установке, поэтому я сделал следующее:

  1. Загрузил их суть в features/support/cucumber_external_resque_worker.rb
  2. Создан инициализатор Rails на config/initializers/cucumber_external_resque.rb, который сделал следующее:
    1. require 'features/support/cucumber_external_resque_worker'
    2. CucumberExternalResqueWorker.install_hooks_on_startup
  3. In cucumber_external_resque_worker.rb, я изменил экземпляры Rails.env.cucumber? на Rails.env.test?, потому что Cucumber запускал функции в среде test (я сделал несколько puts Rails.env в cucumber_external_resque_worker.rb, чтобы быть уверенным.
  4. Я запускаюНа этом этапе я застреваю, потому что я получаю ошибку uninitialized constant WorkerBase (NameError). Возможно, Resque изменил способ, которым он называет вещи.

Спасибо, яN заранее!

Ответы [ 3 ]

6 голосов
/ 11 ноября 2012

Вы можете просто синхронно запустить задание Resque, установив

Resque.inline = true

Я добавил эту строку в мой config/initializers/resque.rb:

Resque.inline = Rails.env.test?

Это более кратко, чем другие подходы, предложенные в посте. Поскольку это синхронно, это будет немного медленнее.

3 голосов
/ 08 сентября 2011

Я потратил некоторое время, играя с этим сегодня, и думаю, что у меня есть решение.

Вот обновленный Gist , который устраняет необходимость в WorkerBase.

также включает изменения конфигурации, необходимые для обеспечения работоспособности (которые идентичны обнаруженным вами изменениям).


# This is adapted from this gist: https://gist.github.com/532100 by Square
# The main difference is that it doesn't require Bluth for WorkerBase
# It also calls prune_dead_workers on start so it doesn't hang on every other run
# It does not do anything special to avoid connecting to your main redis instance; you should be
# doing that elsewhere

class CucumberExternalResqueWorker
  DEFAULT_STARTUP_TIMEOUT = 1.minute
  COUNTER_KEY = "cucumber:counter"

  class << self
    attr_accessor :pid, :startup_timeout

    def start
      # Call from a Cucumber support file so it is run on startup
      return unless Rails.env.test?
      if self.pid = fork
        start_parent
        wait_for_worker_to_start
      else
        start_child
      end
    end

    def install_hooks_on_startup
      # Call from a Rails initializer
      return unless Rails.env.test?
      # Because otherwise crashed workers cause a fork and we pause the actual worker forever
      Resque::Worker.all.each { |worker| worker.prune_dead_workers }
      install_pause_on_start_hook
      install_worker_base_counter_patch
    end

    def process_all
      # Call from a Cucumber step
      unpause
      sleep 1 until done?
      pause
    end

    def incr
      Resque.redis.incr(COUNTER_KEY)
    end

    def decr
      Resque.redis.decr(COUNTER_KEY)
    end

    def reset_counter
      Resque.redis.set(COUNTER_KEY, 0)
    end

    private

    def done?
      Resque.redis.get(CucumberExternalResqueWorker::COUNTER_KEY).to_i.zero?
    end

    def pause(pid = self.pid)
      return unless Rails.env.test?
      Process.kill("USR2", pid)
    end

    def unpause
      return unless Rails.env.test?
      Process.kill("CONT", pid)
    end

    def start_parent
      at_exit do
        #reset_counter
        Process.kill("KILL", pid) if pid
      end
    end

    def start_child
      # Array form of exec() is required here, otherwise the worker is not a direct child process of cucumber.
      # If it's not the direct child process then the PID returned from fork() is wrong, which means we can't
      # communicate with the worker.
      exec('rake', 'resque:work', "QUEUE=*", "RAILS_ENV=test", "VVERBOSE=1")
    end

    def wait_for_worker_to_start
      self.startup_timeout ||= DEFAULT_STARTUP_TIMEOUT
      start = Time.now.to_i
      while (Time.now.to_i - start) < startup_timeout
        return if worker_started?
        sleep 1
      end

      raise "Timeout while waiting for the worker to start. Waited #{startup_timeout} seconds."
    end

    def worker_started?
      Resque.info[:workers].to_i > 0
    end

    def install_pause_on_start_hook
      Resque.before_first_fork do
        #reset_counter
        pause(Process.pid)
      end
    end

    def install_worker_base_counter_patch
      Resque.class_eval do
        class << self
          def enqueue_with_counters(*args, &block)
            CucumberExternalResqueWorker.incr
            enqueue_without_counters(*args, &block)
          end
          alias_method_chain :enqueue, :counters
        end
      end
      Resque::Job.class_eval do
        def perform_with_counters(*args, &block)
          perform_without_counters(*args, &block)
        ensure
          CucumberExternalResqueWorker.decr
        end
        alias_method_chain :perform, :counters
      end
    end
  end
end

В файле среды Cucumber features/support/env.rb

После:

require 'cucumber/rails'

Добавить:

require 'lib/cucumber_external_resque_worker'
CucumberExternalResqueWorker.start

Изменить:

  DatabaseCleaner.strategy = :transaction

на:

  DatabaseCleaner.strategy = :truncation

Также создать файл: config/initializers/cucumber_external_resque.rb

require 'lib/cucumber_external_resque_worker'
CucumberExternalResqueWorker.install_hooks_on_startup
# In my controller, I have:
  def start
    if params[:job] then
      Resque.enqueue(DemoJob, j.id)
    end
    redirect_to :action => :index
  end
# DemoJob:
class DemoJob
  def self.queue
    :demojob
  end
  def perform(job_id)
    j = Job.find(job_id)
    j.value = "done"
    j.save
  end
# In your actual Cucumber step file, for instance:
When /I click the start button for "([^"]*)"/ do |jobname|
  CucumberExternalResqueWorker.reset_counter
  Resque.remove_queue(DemoJob.queue)
  click_button(jobname)
end
When /all open jobs are processed/ do
  CucumberExternalResqueWorker.process_all
end

# And you cucumber feature file looks like:
# Scenario: Starting a job
#   When I click the start button for "Test Job 1"
#     And all open jobs are processed
#   Then I am on the job index page
#     And I should see an entry for "Test Job 1" with a value of "done".
0 голосов
/ 05 августа 2011

Я пробую подход, описанный здесь:

Работа с огурцом и Resque

, который выполняет обработку Resque синхронно.В моем случае я не заинтересован в тестировании Resque, я просто хочу проверить функциональность в моем приложении.

...