Are spies an appropriate approach to see if Resque methods are being fired?

后端 未结 1 1313
忘了有多久
忘了有多久 2021-01-29 06:45

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

相关标签:
1条回答
  • 2021-01-29 07:13

    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:

    • Spies - Basics - RSpec Mocks - RSpec - Relish
    • Allowing messages - Basics - RSpec Mocks - RSpec - Relish
    • A Closer Look at Test Spies
    0 讨论(0)
提交回复
热议问题