TDD 模式开发 Sidekiq 为后端的 ActiveJob 任务队列
这篇文章介绍两个知识点
- 在 Rails 5.0.1 中设置 Active Job 和 sidekiq
- 为 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
参考链接: