Home > OS >  How to fake a Job method in rspec
How to fake a Job method in rspec

Time:03-18

I have a Rails model which is deactivated by default. A Sidekiq job is activating it after success. All my specs are failing after implementing this job. How can I "stub" / "fake" that database change to get my specs falling without changing all of them to Model.unscoped

Maybe somehow a helper_function could be called to mock that job function.

class Model 
  attribute :active # false by default in Database
  default_scope { where(:active => true) }
  after_create :run_job, if: -> { saved_change_to_something? }


  def run_job
    MyJob.perform_async(id)
  end

  def mark_visible!
    update_column(:active, true)
  end


  def stuff_to_do_in_job
    update_column(:active, false)

    # do long term stuff
  end

end

class MyJob
  include Sidekiq::Job

  def perform(id)
    model = Model.find(id)
    if stuff_to_do_in_job
      model.mark_visible!
    end
end



RSpec.describe Model, type: :model do
  it "is active after saved"
    model = Model.new(model_attributes)

    model.save
    # --> • <---  somehow mock here a running job, setting model to true
    expect(model.active).to be_truthy

end

Of course I can change all functions where any Model.find is needed to set to Model.unscoped.find but that looks awkward

Do I have a test modelling issue?

CodePudding user response:

I don't think you would want to mock the job after each Model creation. Just create the Model with active: true in your specs that do not test that specific behaviour.

Then you can create one spec to make sure that the job is being scheduled i.e.:

expect{ model.save }.to have_enqueued_job(MyJob)

And finally create a new file for the specs of MyJob, where you assert that it is doing what it should do when executed.

CodePudding user response:

I got it!!! Thank you for all your suggestions.

It was a design problem:

What I did was to deactivate the Model in the model itself, and after that fired a job, where within this job it was activated again.

The way I am doing it now, is deactivating it within the Model.

There are two ways:

  • Deactivate it within the Job and at the end activating it again or
  • Deactivating it right before calling the Job and activating it at the end of the job.
class Model 
  attribute :active # false by default in Database
  default_scope { where(:active => true) }
  after_create :run_job, if: -> { saved_change_to_something? }


  def run_job
    # or deactivate it here
    mark_invisible!

    MyJob.perform_async(id)
  end

  def mark_visible!
    update_column(:active, true)
  end

  def mark_invisible!
    update_column(:active, false)
  end


  def stuff_to_do_in_job
    # don't do it here mark_invisible!
    # do long term stuff
  end

end

class MyJob
  include Sidekiq::Job

  def perform(id)
    model = Model.find(id)
    model.mark_invisible! # either here
    if stuff_to_do_in_job
      model.mark_visible!
    end
end
  • Related