2

I am trying to write some specs for RSpec + Sidekiq in a Rails 4.2.4 app, but am encountering some issues.

My code looks like this:

class MyImportJob
  include Sidekiq::Worker
  sidekiq_options queue: :default

  def perform(params)
    # Do magic
  end
end

and the spec:

describe MyImportJob, type: :job do
  let(:panel) { create(:panel) }

  describe '#perform' do
    context 'unsuccessfully' do
      it 'raises ArgumentError if no panel param was passed' do
        expect {subject.perform_async()}.to raise_error(ArgumentError)
      end
    end

    context 'successfully' do
      it 'given a panel, it increases the job number' do
        expect {
          subject.perform_async(panel_id: panel.id)
        }.to change(subject.jobs, :size).by(1)
      end
    end
  end
end

But I am receiving the following errors:

Failure/Error: }.to change(subject.jobs, :size).by(1)
  NoMethodError:
    undefined method `jobs' for #<MyImportJob:0x007f80b74c5c18>

and

Failure/Error: expect {subject.perform_async()}.to raise_error(ArgumentError)
  expected ArgumentError, got #<NoMethodError: undefined method `perform_async' for #<MyImportJob:0x007f80b6d73f50>>

I believe perform_async should be provided by default by Sidekiq as long as I include the line include Sidekiq::Worker in my worker, is this correct? The first test passes if I just use perform but I'd expect it to pass with perform_async which is what I'm using in my codebase.

As for the second, I don't understand why there is no method jobs for the test subject. Any clue about that?

My rails_helper.rb file has:

require 'sidekiq/testing'
Sidekiq::Testing.fake!

Thanks in advance!

DaniG2k
  • 4,772
  • 36
  • 77
  • Sidenote: the tests in this question are rather useless. You're testing functionality of sidekiq (duplicating the effort). It's quite safe to assume that sidekiq "just works", so your tests should check your actual logic ("check that welcome email is scheduled if and only if a valid signup form was posted") – Sergio Tulentsev Jan 31 '18 at 16:24
  • Yep that is the next step, I was just wondering how I could get the basic tests to even work. Now I know that it's `subject`s fault. Thanks for the advice! – DaniG2k Jan 31 '18 at 16:26
  • Yep, baby steps. Just making sure. :) – Sergio Tulentsev Jan 31 '18 at 16:27

2 Answers2

6

In case you don't define subject explicitly, rspec will create subject as following rule:

By default, if the first argument to an outermost example group (describe or context block) is a class, RSpec creates an instance of that class and assigns it to the subject

ref: What's the difference between RSpec's subject and let? When should they be used or not?

That means it create instance of your worker. So that you can't call perform_async and jobs.

To resolve your issue, define it explicitly as below:

describe MyImportJob, type: :job do
  let(:panel) { create(:panel) }

  subject { MyImportJob }

  describe '#perform' do
    context 'unsuccessfully' do
      it 'raises ArgumentError if no panel param was passed' do
        expect {subject.perform_async()}.to raise_error(ArgumentError)
      end
    end

    context 'successfully' do
      it 'given a panel, it increases the job number' do
        expect {
          subject.perform_async(panel_id: panel.id)
        }.to change(subject.jobs, :size).by(1)
      end
    end
  end
end
Tai
  • 1,244
  • 9
  • 11
1

expected ArgumentError, got #<NoMethodError: undefined method 'perform_async' for #<MyImportJob:0x007f80b6d73f50>>

perform_async is a method on worker class itself.

MyImportJob.perform_async(...)

I don't understand why there is no method jobs for the test subject

The same exact reason. It's a method on the worker class.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367