Panpaper
Panpaper

Reputation: 571

Rspec how to test a Sidekiq job?

I have a Sidekiq job that looks something like this:

class Arbitrary::MarkSold < ApplicationJob
  def perform(item_id)
    return if Rails.env.test?
    item = Item.find_by(item_id)
    item.sold = true
    item.save
  end
end

And a corresponding RSpec test that looks like this:

Rspec.describe Arbitrary::MarkSold, type: :job do
  describe 'perform' do
    it 'runs' do
      expect(Arbitrary::Marksold).to receive(:perform).and_return(nil)
      MarkSold.new.perform(34)
    end
  end
end

When I try to run this test, I get a failure with this error:

Arbitrary::MarkSold does not implement: perform`.

However, I can clearly see that Arbitrary::MarkSold has a perform method.

I've read Method Stubs but couldn't make heads or tails out of it, or figure out how to apply it to this situation.

I'd greatly appreciate any pointers or links to documentation other than the one I've linked. As a beginner I find that the rspec docs aren't very beginner-friendly. Thank you in advance!

Ruby version: 2.4.9 Rails version: 5.1.7 RSpec version: 3.7

Upvotes: 1

Views: 6759

Answers (3)

Nicolas
Nicolas

Reputation: 63

For the next users stumbling upon this page from Google (like me)...

If you are looking to test your Sidekiq job, consider that maybe what you are trying to test is the content of perform and if so you can test it directly like any other ruby object.

source: https://github.com/sidekiq/sidekiq/wiki/Testing#testing-workers-directly

Upvotes: 1

Jo&#227;o Fernandes
Jo&#227;o Fernandes

Reputation: 741

There are some issues in what you are doing:

expect(Arbitrary::Marksold).to receive(:perform).and_return(nil)

In the snippet above, you are expecting that the class Arbitrary::Marksold to receive perform, but you are not expecting that an instance should receive perform. That's why you are getting the does not implement: perform error.

You could (I did not test it, so you may have to adjust one or another thing):

marksold_spy = instance_spy(Arbitrary::Marksold)
allow(Arbitrary::Marksold).to(receive(:new).and_return(marksold_spy))
expect(marksold_spy).to(receive(:perform).and_return(nil))

But the above isn't the way how I'd do it. The way I'd do it:

class Arbitrary::MarkSold
  include Sidekiq::Worker

  def perform(item_id)
    item = Item.update(item_id, sold: true)
  end
end

And my test:

Rspec.describe Arbitrary::MarkSold, type: :job do
  describe 'perform' do
    it 'runs' do
      item = create(:item, sold: false)
      Sidekiq::Testing.inline! do
        described_class.perform_async(item.id)
      end
      expect(item.reload.sold).to be_truthy
    end
  end
end

To read a little bit more about Sidekiq::Testing, you can go through this link

Upvotes: 0

dtakeshta
dtakeshta

Reputation: 214

I use have_enqueued_job to test if the job gets enqueued assuming that is what you are trying to test. It seems like it.

https://relishapp.com/rspec/rspec-rails/docs/matchers/have-enqueued-job-matcher

Upvotes: 1

Related Questions