间谍是否是查看 Resque 方法是否被触发的合适方法?
Are spies an appropriate approach to see if Resque methods are being fired?
虽然简单覆盖将此报告为 100% 覆盖,但我并不满意。标记为焦点的规范我想确认所有的 Resque 方法都被触发了。间谍或双重身份是正确的做法吗?
规格
describe 'resque tasks' do
include_context 'rake'
let(:task_paths) { ['tasks/resque'] }
before do
invoke_task.reenable
end
# rubocop:disable all
describe 'resque' do
context ':setup' do
let(:task_name) { 'resque:setup' }
it 'works' do
invoke_task.invoke
expect(Resque.logger.level).to eq(1)
end
end
context ':scheduler_setup' do
let(:task_name) { 'resque:scheduler_setup' }
it 'works' do
expect(invoke_task.invoke).to be
end
end
context ':clear', focus: true do
let(:task_name) { 'resque:clear' }
it 'works' do
expect(Resque).to receive(:remove_queue).with('queue:default').and_return(true)
expect { invoke_task.invoke }.to output(
"Clearing default...\n"\
"Clearing delayed...\n"\
"Clearing stats...\n"\
"Clearing zombie workers...\n"\
"Clearing failed jobs...\n"\
"Clearing resque workers...\n"
).to_stdout
end
end
end
describe 'jobs:work' do
let(:task_name) { 'jobs:work' }
it 'works' do
expect_any_instance_of(Object).to receive(:system).with("bundle exec env rake resque:workers QUEUE='*' COUNT='#{ENV['WEB_WORKERS']}'").and_return(true)
expect(invoke_task.invoke).to be
end
end
# rubocop:enable all
end
Resque Rake 任务
require 'resque'
require 'resque/tasks'
require 'resque/scheduler/tasks'
# http://jademind.com/blog/posts/enable-immediate-log-messages-of-resque-workers/
namespace :resque do
desc 'Initialize Resque environment'
task setup: :environment do
ENV['QUEUE'] ||= '*'
Resque.logger.level = Logger::INFO
end
task scheduler_setup: :environment
# see - old version
# see https://github.com/defunkt/resque/issues/49
# see http://redis.io/commands - new commands
desc 'Clear pending tasks'
task clear: :environment do
queues = Resque.queues
queues.each do |queue_name|
puts "Clearing #{queue_name}..."
Resque.remove_queue("queue:#{queue_name}")
end
puts 'Clearing delayed...'
Resque.redis.keys('delayed:*').each do |key|
Resque.redis.del key.to_s
end
Resque.redis.del 'delayed_queue_schedule'
Resque.reset_delayed_queue
puts 'Clearing stats...'
Resque.redis.set 'stat:failed', 0
Resque.redis.set 'stat:processed', 0
puts 'Clearing zombie workers...'
Resque.workers.each(&:prune_dead_workers)
puts 'Clearing failed jobs...'
cleaner = Resque::Plugins::ResqueCleaner.new
cleaner.clear
puts 'Clearing resque workers...'
Resque.workers.each(&:unregister_worker)
end
end
desc 'Alias for resque:work'
#
task 'jobs:work' do
system("bundle exec env rake resque:workers QUEUE='*' COUNT='#{ENV['WEB_WORKERS']}'")
end
共享上下文
shared_context 'rake' do
let(:invoke_task) { Rake.application[task_name] }
let(:highline) { instance_double(HighLine) }
before do
task_paths.each do |task_path|
Rake.application.rake_require(task_path)
end
Rake::Task.define_task(:environment)
end
before do
allow(HighLine).to receive(:new).and_return(highline)
end
end
The spec marked as focus I would like to confirm that all of the Resque methods are being fired. Is a spy or a double the right approach for this?
是的。此测试中的 Spy 只会测试它是否收到了这些方法调用,因为它充当这些测试的 double
替身;这意味着您没有在此测试中测试任务的行为,您正在测试任务是否有一个对象,例如 Resque
接收这些方法调用。
Spies
Message expectations put an example's expectation at the start, before you've invoked the code-under-test. Many developers prefer using an act-arrange-assert (or given-when-then) pattern for structuring tests. Spies are an alternate type of test double that support this pattern by allowing you to expect that a message has been received after the fact, using have_received
.
您的it 'works'
测试
的示例
it 'works' do
expect(Resque).to receive(:remove_queue).with('queue:default').and_return(true)
expect { invoke_task.invoke }.to output(
"Clearing default...\n"\
"Clearing delayed...\n"\
"Clearing stats...\n"\
"Clearing zombie workers...\n"\
"Clearing failed jobs...\n"\
"Clearing resque workers...\n"
).to_stdout
end
如下
RSpec.describe "have_received" do
it 'works' do
Rake::Task.define_task(:environment)
invoke_task = Rake.application['resque:clear']
redis_double = double("redis")
allow(redis_double).to receive(:keys).with('delayed:*').and_return([])
allow(redis_double).to receive(:del).with('delayed_queue_schedule').and_return(true)
allow(redis_double).to receive(:set).with('stat:failed', 0).and_return(true)
allow(redis_double).to receive(:set).with('stat:processed', 0).and_return(true)
allow(Resque).to receive(:queues).and_return([])
allow(Resque).to receive(:redis).and_return(redis_double)
# allow(Resque).to receive(:remove_queue).with('queue:default') #.and_return(true)
allow(Resque).to receive(:reset_delayed_queue) #.and_return(true)
allow(Resque).to receive(:workers).and_return([])
cleaner_double = double("cleaner")
allow(Resque::Plugins::ResqueCleaner).to receive(:new).and_return(cleaner_double)
allow(cleaner_double).to receive(:clear).and_return(true)
expect { invoke_task.invoke }.to output(
# "Clearing default...\n"\
"Clearing delayed...\n"\
"Clearing stats...\n"\
"Clearing zombie workers...\n"\
"Clearing failed jobs...\n"\
"Clearing resque workers...\n"
).to_stdout
expect(redis_double).to have_received(:keys)
expect(redis_double).to have_received(:del)
expect(redis_double).to have_received(:set).with('stat:failed', 0)
expect(redis_double).to have_received(:set).with('stat:processed', 0)
expect(Resque).to have_received(:queues)
expect(Resque).to have_received(:redis).at_least(4).times
# expect(Resque).to have_received(:remove_queue).with('queue:default')
expect(Resque).to have_received(:reset_delayed_queue)
expect(Resque).to have_received(:workers).twice
expect(Resque::Plugins::ResqueCleaner).to have_received(:new)
expect(cleaner_double).to have_received(:clear)
end
end
备注:
allow(Resque).to receive(:remove_queue).with('queue:default')
被注释掉了,因为 allow(redis_double).to receive(:keys).with('delayed:*').and_return([])
return 在我的示例代码中是一个空数组,这意味着 queues.each
永远不会迭代一次,所以 Resque.remove_queue("queue:#{queue_name}")
永远不会被调用并且 "Clearing default...\n"\
不是 return 对于预期的输出
另外,在这个任务中发生了很多事情,可能值得将它分解成更小的任务。
这有效地存根了 Resque
对象上的每个预期方法调用,然后在调用任务后访问双打接收那些预期的方法调用。它不测试这些任务的结果,只测试发生的方法调用并确认那些
methods are being fired.
参考文献:
虽然简单覆盖将此报告为 100% 覆盖,但我并不满意。标记为焦点的规范我想确认所有的 Resque 方法都被触发了。间谍或双重身份是正确的做法吗?
规格
describe 'resque tasks' do
include_context 'rake'
let(:task_paths) { ['tasks/resque'] }
before do
invoke_task.reenable
end
# rubocop:disable all
describe 'resque' do
context ':setup' do
let(:task_name) { 'resque:setup' }
it 'works' do
invoke_task.invoke
expect(Resque.logger.level).to eq(1)
end
end
context ':scheduler_setup' do
let(:task_name) { 'resque:scheduler_setup' }
it 'works' do
expect(invoke_task.invoke).to be
end
end
context ':clear', focus: true do
let(:task_name) { 'resque:clear' }
it 'works' do
expect(Resque).to receive(:remove_queue).with('queue:default').and_return(true)
expect { invoke_task.invoke }.to output(
"Clearing default...\n"\
"Clearing delayed...\n"\
"Clearing stats...\n"\
"Clearing zombie workers...\n"\
"Clearing failed jobs...\n"\
"Clearing resque workers...\n"
).to_stdout
end
end
end
describe 'jobs:work' do
let(:task_name) { 'jobs:work' }
it 'works' do
expect_any_instance_of(Object).to receive(:system).with("bundle exec env rake resque:workers QUEUE='*' COUNT='#{ENV['WEB_WORKERS']}'").and_return(true)
expect(invoke_task.invoke).to be
end
end
# rubocop:enable all
end
Resque Rake 任务
require 'resque'
require 'resque/tasks'
require 'resque/scheduler/tasks'
# http://jademind.com/blog/posts/enable-immediate-log-messages-of-resque-workers/
namespace :resque do
desc 'Initialize Resque environment'
task setup: :environment do
ENV['QUEUE'] ||= '*'
Resque.logger.level = Logger::INFO
end
task scheduler_setup: :environment
# see - old version
# see https://github.com/defunkt/resque/issues/49
# see http://redis.io/commands - new commands
desc 'Clear pending tasks'
task clear: :environment do
queues = Resque.queues
queues.each do |queue_name|
puts "Clearing #{queue_name}..."
Resque.remove_queue("queue:#{queue_name}")
end
puts 'Clearing delayed...'
Resque.redis.keys('delayed:*').each do |key|
Resque.redis.del key.to_s
end
Resque.redis.del 'delayed_queue_schedule'
Resque.reset_delayed_queue
puts 'Clearing stats...'
Resque.redis.set 'stat:failed', 0
Resque.redis.set 'stat:processed', 0
puts 'Clearing zombie workers...'
Resque.workers.each(&:prune_dead_workers)
puts 'Clearing failed jobs...'
cleaner = Resque::Plugins::ResqueCleaner.new
cleaner.clear
puts 'Clearing resque workers...'
Resque.workers.each(&:unregister_worker)
end
end
desc 'Alias for resque:work'
#
task 'jobs:work' do
system("bundle exec env rake resque:workers QUEUE='*' COUNT='#{ENV['WEB_WORKERS']}'")
end
共享上下文
shared_context 'rake' do
let(:invoke_task) { Rake.application[task_name] }
let(:highline) { instance_double(HighLine) }
before do
task_paths.each do |task_path|
Rake.application.rake_require(task_path)
end
Rake::Task.define_task(:environment)
end
before do
allow(HighLine).to receive(:new).and_return(highline)
end
end
The spec marked as focus I would like to confirm that all of the Resque methods are being fired. Is a spy or a double the right approach for this?
是的。此测试中的 Spy 只会测试它是否收到了这些方法调用,因为它充当这些测试的 double
替身;这意味着您没有在此测试中测试任务的行为,您正在测试任务是否有一个对象,例如 Resque
接收这些方法调用。
Spies
Message expectations put an example's expectation at the start, before you've invoked the code-under-test. Many developers prefer using an act-arrange-assert (or given-when-then) pattern for structuring tests. Spies are an alternate type of test double that support this pattern by allowing you to expect that a message has been received after the fact, using
have_received
.
您的it 'works'
测试
it 'works' do
expect(Resque).to receive(:remove_queue).with('queue:default').and_return(true)
expect { invoke_task.invoke }.to output(
"Clearing default...\n"\
"Clearing delayed...\n"\
"Clearing stats...\n"\
"Clearing zombie workers...\n"\
"Clearing failed jobs...\n"\
"Clearing resque workers...\n"
).to_stdout
end
如下
RSpec.describe "have_received" do
it 'works' do
Rake::Task.define_task(:environment)
invoke_task = Rake.application['resque:clear']
redis_double = double("redis")
allow(redis_double).to receive(:keys).with('delayed:*').and_return([])
allow(redis_double).to receive(:del).with('delayed_queue_schedule').and_return(true)
allow(redis_double).to receive(:set).with('stat:failed', 0).and_return(true)
allow(redis_double).to receive(:set).with('stat:processed', 0).and_return(true)
allow(Resque).to receive(:queues).and_return([])
allow(Resque).to receive(:redis).and_return(redis_double)
# allow(Resque).to receive(:remove_queue).with('queue:default') #.and_return(true)
allow(Resque).to receive(:reset_delayed_queue) #.and_return(true)
allow(Resque).to receive(:workers).and_return([])
cleaner_double = double("cleaner")
allow(Resque::Plugins::ResqueCleaner).to receive(:new).and_return(cleaner_double)
allow(cleaner_double).to receive(:clear).and_return(true)
expect { invoke_task.invoke }.to output(
# "Clearing default...\n"\
"Clearing delayed...\n"\
"Clearing stats...\n"\
"Clearing zombie workers...\n"\
"Clearing failed jobs...\n"\
"Clearing resque workers...\n"
).to_stdout
expect(redis_double).to have_received(:keys)
expect(redis_double).to have_received(:del)
expect(redis_double).to have_received(:set).with('stat:failed', 0)
expect(redis_double).to have_received(:set).with('stat:processed', 0)
expect(Resque).to have_received(:queues)
expect(Resque).to have_received(:redis).at_least(4).times
# expect(Resque).to have_received(:remove_queue).with('queue:default')
expect(Resque).to have_received(:reset_delayed_queue)
expect(Resque).to have_received(:workers).twice
expect(Resque::Plugins::ResqueCleaner).to have_received(:new)
expect(cleaner_double).to have_received(:clear)
end
end
备注:
allow(Resque).to receive(:remove_queue).with('queue:default')
被注释掉了,因为allow(redis_double).to receive(:keys).with('delayed:*').and_return([])
return 在我的示例代码中是一个空数组,这意味着queues.each
永远不会迭代一次,所以Resque.remove_queue("queue:#{queue_name}")
永远不会被调用并且"Clearing default...\n"\
不是 return 对于预期的输出另外,在这个任务中发生了很多事情,可能值得将它分解成更小的任务。
这有效地存根了 Resque
对象上的每个预期方法调用,然后在调用任务后访问双打接收那些预期的方法调用。它不测试这些任务的结果,只测试发生的方法调用并确认那些
methods are being fired.
参考文献: