如何使用 Resque 和 Rspec 示例桥接测试? [英] How to bridge the testing using Resque with Rspec examples?

查看:57
本文介绍了如何使用 Resque 和 Rspec 示例桥接测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在与 Rspec 示例并行实现 Resque 时感到困惑.下面是一个带有昂贵方法 .generate(self) 的类类 SomeClass...ChangeGenerator.generate(self)...结束

实现resque后,上面的类变成了下面的,增加了一个ChangeRecorderJob类.

class SomeClass...Resque.enqueue(ChangeRecorderJob, self.id)...结尾类 ChangeRecorderJob@queue = :change_recorder_jobdef self.perform(noti_id)通知 = Notification.find(noti_id)ChangeGenerator.generate(通知)结尾结尾

它完美地工作.但我有两个顾虑.

之前,我的示例规范用于测试 .generate(self) 方法的整个堆栈.但是现在既然我把它推到了 Resque 的工作中,我怎么能在不隔离的情况下桥接我的例子来使相同的测试变成绿色?还是我必须隔离测试??

最后,如果我有 10 个作业要排队,我是否必须使用 self.perform 方法创建 10 个单独的作业类?

解决方案

测试像这样的异步内容总是很棘手.我们要做的是:

  • 在我们的功能测试中,我们确保作业进入队列.使用 mocha 或类似的东西通常就足够了.如果您想运行测试 redis 服务器,您可以验证正确的队列增长以及作业参数是否正确.尽管那时您正在测试 Resque 本身.

  • 作业作为单元测试单独进行测试.由于它们只有一个名为 perform 的类方法,因此您的单元测试非常简单.在您的情况下,您将测试 ChangeRecorderJob.perform 是否符合您的要求.我们倾向于测试作业是否在适当的队列中,作业的参数是否有效,以及作业是否执行我们想要的操作.

  • 现在,一起测试所有内容是棘手的部分.我已经完成了这两种不同的方法,每种方法都有优点和缺点:

    • Monkey-patch Resqueue.enqueue 同步运行作业 从 resque 1.14.0 开始,您可以在初始化程序中使用 Resque.inline = true 而不是猴子补丁
    • 模拟工作人员从队列中弹出作业并实际在分叉进程中运行

到目前为止,同步运行作业是两者中更容易的.您只需在 spec_helper 中加载如下内容:

<前>模块请求alias_method :enqueue_async, :enqueuedef self.enqueue(klass, *args)klass.new(0, *args).perform结尾结尾

从 resque 1.14.0 开始,您只需在初始化程序中设置 Resque.inline = true 而不是猴子补丁.如果您坚持使用旧版本的 resque,则需要使用猴子补丁.

请注意,由于您在此处同步运行,因此您将承担长期运行作业的成本.也许更重要的是,您将在同一个流程中运行,因此它不能完全准确地表示您的工作将如何运行.

要在分叉的 worker 中运行作业,就像 resque 一样,您需要执行以下操作:

<前>def run_resque_job(job_class, job_args, opts={})queue = opts[:queue] ||测试队列"Resque::Job.create(queue, job_class, *job_args)worker = Resque::Worker.new(队列)worker.very_verbose = true 如果 opts[:verbose]如果选择 [:fork]# 做一个工作然后关闭def worker.done_working极好的关掉结尾工人.工作(0.01)别的工作 = worker.reserve工人.执行(工作)结尾结尾

让工作程序从队列中弹出作业有一点延迟.很自然地,您需要运行一个测试 redis 服务器,以便工作人员有一个队列可以弹出.

我相信其他人已经想出了测试resque作业的聪明方法.这些都是对我有用的.

I've a confusion while implementing Resque in parallel with Rspec examples. The following is a class with expensive method .generate(self) class SomeClass ... ChangeGenerator.generate(self) ... end

After implementing resque, the above class changed to the following and added a ChangeRecorderJob class.

class SomeClass
  ...
  Resque.enqueue(ChangeRecorderJob, self.id)
  ...
end

class ChangeRecorderJob
  @queue = :change_recorder_job

  def self.perform(noti_id)
    notification = Notification.find(noti_id)    
    ChangeGenerator.generate(notification)
  end
end

It works perfectly. But I have 2 concerns.

Before, my example spec used to test the whole stack of .generate(self) method. But now since I pushed that into Resque job, how can I bridge my examples to make that same test green without isolating? Or do I have to isolate the test??

And lastly, if I have 10 jobs to enque, do I have to create 10 separate job classes with self.perform method?

解决方案

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.

这篇关于如何使用 Resque 和 Rspec 示例桥接测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆