3

I would like to test some resque workers, and the actions that enqueue jobs with these workers. I am using rspec and rails.

Currently I have a model, let's call it Article.rb, that has a before_save method called updates_related_categories that checks if a job with CategoriesSorter needs to be enqueued. If so, it enqueues a job with that worker, with the argument of the category id that the article is related to.

In test, however, these jobs are sent to the same queue as the development server sends jobs to. (i check using the resque server that you can tie into your server at root/redis/overview)

I want to know:

1) How can I send test jobs to a different queue than the development jobs?

If this is possible, any other advice on testing resque is also welcome.

I have seen some related questions that suggest resque-unit and resque-spec but these are pretty undeveloped, and I couldn't get them to a useful working state. Also, I have heard of using Resque.inline but I don't know if that is relevant in this case, as resque isn't called in test specs, it's called from the article model on save of objects created in test.

Sample Code:

Article.rb:

 before_save :update_related_categories
 def update_related_categories
     #some if statements/checks to see if a related category needs updating
         Resque.enqueue(CategoriesWorker, [category_id])
     end
 end

CategoriesSorter:

 class CategoriesSorter
     @queue=:sorting_queue
     def self.perform(ids)
        ids.each do |id|
           #some code
        end
      end
 end

Specs:

 it "should do something" do
     @article = #set something to enqueue a job
     @article.save
     #can i check if a job is enqueued? can i send this job NOT to the development queue but a different one?
 end
jay
  • 12,066
  • 16
  • 64
  • 103

3 Answers3

6

As I see it, you don't want to test whether enqueueing the job results in perform being called - we trust Resque does what it should. However, you can test that 1. calling update_related_categories enqueues the job. Then you can test, separately, whether 2. a worker calling perform results in the desired behavior.

For testing Resque in general, a combination of resque-spec and simulating a worker can accomplish the above two goals.

For 1, with resque-spec, you can simulate a worker calling the perform method on your class, and then check that it has been enqueued correctly:

describe "Calling update_related_categories " do
  before(:each) do
    ResqueSpec.reset!
  end

  it "should enqueue the job" do
    Article.update_related_categories
    CategoriesSorter.should have_queue_size_of(1)
  end
end

For 2, you can create a Job (and specify a separate queue name than your development queue), a Resque::Worker and then assign the Worker to the Job:

def run_worker_simulation
  # see Resque::Job api for setting the args you want
  Resque::Job.create('test_queue_name', 'class_name', 'type', 'id')

  worker = Resque::Worker.new('test_queue_name')
  worker.very_verbose = true

  job = worker.reserve
  worker.perform(job)
end

Hope that gives you some ideas.

ysbecca
  • 131
  • 5
1

You shouldn't test resque, that's the resque development team job, your application should only test that your perfom method does what it have to do. You can also test that your model Article.rb enqueues the job as you want. Sending real jobs to resque is useless, your test will end and the reque queue will be full of useless jobs.

Do something like:

describe 'Article' do
  before(:each) do
    Resque.stub!(:enqueue)
  end

  it 'enqueues the job' do
    cat_id = 1
    Resque.should_receive(:enqueue).once.with(CategoriesWorker, [cat_id])
    Article.create(:category_id => cat_id)
  end
end

describe 'CategoriesSorter' do
  it 'sorts the categories' do
    result = CategoriesSorter.perform([1,4,6,3,2])
    result.should == [1,2,3,4,6]
  end
end

stub or mock unneeded methods/classes

EDIT: also, as you test Article, it's good that you set a before(:each) filter to stub resque so your spec never send jobs to the real queue, i've edited my answer

arieljuod
  • 15,460
  • 2
  • 25
  • 36
1

I have

Resque.inline = ENV['RAILS_ENV'] == "test"

This makes all the resque tasks as inline in test environment.

For testing each Job class , I have separate specs to test perform method of each job separately.

Rahul garg
  • 9,202
  • 5
  • 34
  • 67