Testing asynchronous stuff like this is always tricky. What we do is:
In our functional tests we make sure the job gets enqueued. Using mocha or something similar with an expectation is normally sufficient. If you want to run a test redis server, you could verify the correct queue grew and that the job params are correct. Although you're testing Resque itself a bit at that point.
Jobs are tested in isolation as unit tests. Since they just have a class method called perform
, your unit tests are quite simple. In your case you'd test that ChangeRecorderJob.perform does what you want. We tend to test that jobs are on the appropriate queue, that the params to the job are valid, and that the job does what we want.
Now, to test everything together is the tricky part. I've done this two different ways and each has pros and cons:
Monkey-patch Resqueue.enqueue to run the job synchronously As of resque 1.14.0 you can use Resque.inline = true
in your initializer instead of monkey-patching
- Simulate a worker popping a job off the queue and actually run in a forked process
Running the job synchronously is by far the easier of the two. You'd just load something like the following in your spec_helper:
module Resque
alias_method :enqueue_async, :enqueue
def self.enqueue(klass, *args)
klass.new(0, *args).perform
end
end
As of resque 1.14.0 you can just set Resque.inline = true
in your initializer instead of monkey-patching. If you're stuck on an older version of resque, the monkey-patch is necessary.
Note that because you are running synchronously here, you're going to incur the cost of your long-running job. Perhaps more importantly is you're going to be running in the same process so it's not a completely accurate representation of how your job is going to run.
To run the job in a forked worker, much like resque would, you'll need to do something like the following:
def run_resque_job(job_class, job_args, opts={})
queue = opts[:queue] || "test_queue"
Resque::Job.create(queue, job_class, *job_args)
worker = Resque::Worker.new(queue)
worker.very_verbose = true if opts[:verbose]
if opts[:fork]
# do a single job then shutdown
def worker.done_working
super
shutdown
end
worker.work(0.01)
else
job = worker.reserve
worker.perform(job)
end
end
There's a slight delay in getting the worker to pop the job off the queue. And naturally you'll need to have a test redis server running so that the worker has a queue to pop off of.
I'm sure other people have come up with clever ways of testing resque jobs. These are what have been working for me.