间谍是查看 Resque 方法是否被触发的合适方法吗? [英] Are spies an appropriate approach to see if Resque methods are being fired?
问题描述
虽然简单的覆盖率报告为 100% 覆盖,但我并不满意.标记为焦点的规范我想确认所有 Resque 方法都被触发.间谍或双重身份是解决此问题的正确方法吗?
While simple coverage is reporting this as 100% covered I am not satisfied. 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?
规格
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 http://stackoverflow.com/questions/5880962/how-to-destroy-jobs-enqueued-by-resque-workers - 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'
# http://stackoverflow.com/questions/10424087/resque-multiple-workers-in-development-mode
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
推荐答案
标记为焦点的规范我想确认所有 Resque 方法都被触发.间谍或双重身份是解决此问题的正确方法吗?
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?
是的.这个测试中的间谍只会测试它是否收到了这些方法调用,因为它充当这些测试的 double
替身;这意味着您在此测试中不是在测试任务的行为,而是在测试任务是否具有像 Resque
这样的对象来接收这些方法调用.
Yes. A Spy in this test would only be testing that it received those methods calls, since it is acting as a double
stand-in for those tests; meaning you are not testing the behaviour of task in this test, you are testing that the task has an object like Resque
receiving those method calls.
消息期望 将示例的期望放在开头, 在您调用被测代码之前.许多开发人员更喜欢使用行为-安排-断言(或给出时间-然后)模式来构建测试.间谍是另一种类型的测试替身,它支持这种模式,它允许您使用 have_received
预期在事后已收到消息.
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
.
-- 间谍 - 基础 - RSpec Mocks - RSpec - Relish
对于您的 it 'works'
测试来说这可能是什么样子的示例
An example of what this might look like for your it 'works'
test
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([])
在我的示例代码中返回一个空数组,这意味着queues.each
从不迭代一次,所以Resque.remove_queue("queue:#{queue_name}")
永远不会被调用并且"Clearing default...\n"\
不会返回预期的输出
The
allow(Resque).to receive(:remove_queue).with('queue:default')
is commented out sinceallow(redis_double).to receive(:keys).with('delayed:*').and_return([])
returns an empty array in my example code, meaning thatqueues.each
never iterates once, soResque.remove_queue("queue:#{queue_name}")
is never called and"Clearing default...\n"\
is not return for the expected output
此外,在这个任务中发生了很多事情,可能值得将其分解为更小的任务.
这有效地在 Resque
对象上存根每个预期的方法调用,然后在调用任务后访问双打接收那些预期的方法调用.它不测试这些任务的结果,只测试发生的方法调用并确认这些
This effectively stubs each of the expected method calls on the Resque
object and then accesses after task has been invoked that the doubles receive those expected method calls. It does not test the outcomes of those tasks, only that method calls occurred and confirms those
正在触发方法.
参考:
这篇关于间谍是查看 Resque 方法是否被触发的合适方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!