TDD 模式开发 Sidekiq 为后端的 ActiveJob 任务队列

这篇文章介绍两个知识点

  1. 在 Rails 5.0.1 中设置 Active Job 和 sidekiq
  2. 为 Active Job 编写 Rspec 测试

Sidekiq 设置

添加 sidekiq 到 Gemfile

gem 'sidekiq'

修改 config/application.rb 设置 Backend Adapter

class Application < Rails::Application
  # ...
  config.active_job.queue_adapter = :sidekiq
end

接着添加 sidekiq 进程的配置文件,新建 config/sidekiq.yml 文件。

:verbose: false
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:concurrency: 1
:queues:
  - default

请注意,concurrency 设置的值应比 config/database.yml 中的 pool 小,详细请查看 Sidekiq concurrency 设置和一些注意事项

然后就是 redis 设置,新建 config/initializers/sidekiq.rb 文件

Sidekiq.configure_client do |config|
  config.redis = { url: 'redis://127.0.0.1:6379/0/cache', namespace: 'blog_server'  }
end

Sidekiq.configure_server do |config|
  config.redis = { url: 'redis://127.0.0.1:6379/0/cache', namespace: 'blog_server'  }
end

Sidekiq 服务操作

  • 启动队列服务
    RAILS_ENV=development sidekiq -C config/sidekiq.yml -d
  • 停止队列服务
    sidekiqctl stop tmp/pids/sidekiq.pid 0

编写 Active Job

使用 Rails 生成器生成新的 Job 文件和 Rspec 测试文件

shell command: rails g job email

# app/jobs/email_job.rb
class EmailJob < ApplicationJob
  queue_as :default

  def perform(*args)
    # Do something later
  end
end

更多关于在 Active Job 中使用 sidekiq ,请查看 the official wiki in Sidekiq.

Rspec 测试

在编写 ActiveJob 测试之前,需要在 spec/rails_helper.rb 设置

RSpec.configure do |config|
  config.include ActiveJob::TestHelper, type: :job
  # ...
end

这样的好处就是:不需要在每个 ActiveJob 的测试文件中添加:

include ActiveJob::TestHelper

另外,我们在Repsc 测试环境中,我们需要设置 ActiveJob::Base.queue_adapter 为 test,可以参考 the test adapter must be set to :test 看看。另外通过 stackoverflow 的答案 How to check what is queued in ActiveJob using Rspec 我们可以像下面这样设置,这样减少每个测试文件中都要添加设置代码

Update 1: As noticed within a comment. ActiveJob::Base.queue_adapter.enqueued_jobs works only by setting it the queue_adapter into test mode.
config.active_job.queue_adapter = :test

接着,测试 ActiveJob 的 Rspec 模版如下,在实际开发过程中稍微修改即可用了。

require 'rails_helper'

RSpec.describe ExampleJob, type: :job do
  subject(:job) { described_class.perform_later(123) }

  it 'queues the job' do
    expect { job }
      .to change(ActiveJob::Base.queue_adapter.enqueued_jobs, :size).by(1)
  end

  it 'is in urgent queue' do
    expect(MyJob.new.queue_name).to eq('urgent')
  end

  it 'executes perform' do
    perform_enqueued_jobs { job }
    # expect xxxx
  end

  it 'handles no results error' do
    allow(MyService).to receive(:call).and_raise(NoResultsError)

    perform_enqueued_jobs do
      expect_any_instance_of(MyJob)
        .to receive(:retry_job).with(wait: 10.minutes, queue: :default)

      job
    end
  end

  after do
    clear_enqueued_jobs
    clear_performed_jobs
  end
end

参考链接:

0 条评论
您想说点什么吗?