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
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?
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.
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
.-- Spies - Basics - RSpec Mocks - RSpec - Relish
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
Is as follows
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
Notes:
The allow(Resque).to receive(:remove_queue).with('queue:default')
is commented out since allow(redis_double).to receive(:keys).with('delayed:*').and_return([])
returns an empty array in my example code, meaning that queues.each
never iterates once, so Resque.remove_queue("queue:#{queue_name}")
is never called and "Clearing default...\n"\
is not return for the expected output
Also, there is a lot happening in this one task, and might be worthwhile breaking it down into smaller tasks.
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
methods are being fired.
References: