RSpec: Avoid using allow any instance of to receive - ruby-on-rails

I'm working on one old part of code.
before do
allow_any_instance_of(SportRateManager)
.to receive(:create)
.and_return(true)
end
There is Rubocop error like:
Avoid stubbing using 'allow_any_instance_of'
I read about RuboCop::RSpec:AnyInstance and I tried to change it like bellow.
From this
before do
allow_any_instance_of(SportRateManager)
.to receive(:create)
.and_return(true)
end
To this:
let(:sport_manager) { instance_double(SportRateManager) }
before do
allow(SportRateManager).to receive(:new).and_return(sport_manager)
allow(sport_manager).to receive(:create).and_return(true)
end
And with full context:
- before
describe 'POST create' do
let(:sport_rate) { build(:sport_rate) }
let(:action) { post :create, sport_rate: sport_rate.attributes }
context 'when sport rate manager created the rate successfully' do
before do
allow_any_instance_of(SportRateManager)
.to receive(:create)
.and_return(true)
end
it 'returns ok status' do
action
expect(response).to have_http_status(:ok)
end
end
... - after:
describe 'POST create' do
let(:sport_rate) { build(:sport_rate) }
let(:action) { post :create, sport_rate: sport_rate.attributes }
let(:sport_manager) { instance_double(SportRateManager) }
context 'when sport rate manager created the sport successfully' do
before do
allow(SportRateManager).to receive(:new).and_return(sport_manager)
allow(sport_manager).to receive(:create).and_return(true)
end
it 'returns ok status' do
action
expect(response).to have_http_status(:ok)
end
end
But this doesn't pass the test with error:
#<InstanceDouble(SportRateManager) (anonymous)> received unexpected message :sport_rate with (no args)

The solution was almost done. You probably need to add build :sport_rate before create
Sth like that
let(:sport_manager) { instance_double(SportRateManager) }
before do
allow(SportRateManager).to receive(:new).and_return(sport_manager)
allow(sport_manager).to receive(:sport_rate).and_return(build :sport_rate)
allow(sport_manager).to receive(:create).and_return(true)
end

Related

Testing that job has been enqueued multiple times [duplicate]

I have a create action that calls an ActiveJob if the record is successfully saved.
def create
#object = Object.new(importer_params)
respond_to do |format|
if #object.save
MyJob.perform_later( #object.id )
format.html { redirect_to #object, notice: t('.notice') }
else
format.html { render :new }
end
end
end
I want to test that the Job is correctly called in a controller spec.
describe "POST #create" do
it {
expect {
post :create, { object: valid_attributes }
}.to change(Object, :count).by(1)
}
it {
expect {
post :create, { object: valid_attributes }
}.to have_enqueued_job(MyJob)
}
end
But I get
Failure/Error:
expect {
post :create, { object: valid_attributes }
}.to have_enqueued_job(MyJob)
expected to enqueue exactly 1 jobs, but enqueued 0
The first test is passing, so I know the Object is saved successfully. What is the correct way to test that an ActiveJob is enqueued?
If you need to check that your job has been enqueued several times, you can now do this:
expect {
3.times { HelloJob.perform_later }
}.to have_enqueued_job(HelloJob).at_least(2).times
I've always looked at the size of ActiveJob::Base.queue_adapter.enqueued_jobs to test if a job was called. giving the code
it 'does something' do
expect {
post :create, { object: valid_attributes }
}.to change {
ActiveJob::Base.queue_adapter.enqueued_jobs.count
}.by 1
end
You should make sure that you are setting the enqueued_jobs to an empty array after each spec to avoid any unexpected behaviour. You can do this in the spec/rails_helper.rb
In official docs here is have_enqueued_job matcher
The have_enqueued_job (also aliased as enqueue_job) matcher is used to check if given ActiveJob job was enqueued.
https://relishapp.com/rspec/rspec-rails/docs/matchers/have-enqueued-job-matcher

How to test that ActiveJob is enqueued?

I have a create action that calls an ActiveJob if the record is successfully saved.
def create
#object = Object.new(importer_params)
respond_to do |format|
if #object.save
MyJob.perform_later( #object.id )
format.html { redirect_to #object, notice: t('.notice') }
else
format.html { render :new }
end
end
end
I want to test that the Job is correctly called in a controller spec.
describe "POST #create" do
it {
expect {
post :create, { object: valid_attributes }
}.to change(Object, :count).by(1)
}
it {
expect {
post :create, { object: valid_attributes }
}.to have_enqueued_job(MyJob)
}
end
But I get
Failure/Error:
expect {
post :create, { object: valid_attributes }
}.to have_enqueued_job(MyJob)
expected to enqueue exactly 1 jobs, but enqueued 0
The first test is passing, so I know the Object is saved successfully. What is the correct way to test that an ActiveJob is enqueued?
If you need to check that your job has been enqueued several times, you can now do this:
expect {
3.times { HelloJob.perform_later }
}.to have_enqueued_job(HelloJob).at_least(2).times
I've always looked at the size of ActiveJob::Base.queue_adapter.enqueued_jobs to test if a job was called. giving the code
it 'does something' do
expect {
post :create, { object: valid_attributes }
}.to change {
ActiveJob::Base.queue_adapter.enqueued_jobs.count
}.by 1
end
You should make sure that you are setting the enqueued_jobs to an empty array after each spec to avoid any unexpected behaviour. You can do this in the spec/rails_helper.rb
In official docs here is have_enqueued_job matcher
The have_enqueued_job (also aliased as enqueue_job) matcher is used to check if given ActiveJob job was enqueued.
https://relishapp.com/rspec/rspec-rails/docs/matchers/have-enqueued-job-matcher

rspec + shoulda: setting up data

I have the following test. There are three it blocks. The first one doesn't use shoulda unlike the other two.
If I don't use the before block with post :create, product: attrs then the first test fails as expected. But If I put the before block there then the first test fails, but the other two pass. I have a uniqueness validation on product name, but that shouldn't be the problem as I'm using sequence with factory.
What should I do? How should I generally setup the data for testing when there are rspec and shoulda matchers present at the same time?
describe "when user logged in" do
before(:each) do
login_user #logged in user is available by calling #user
end
context "POST create" do
context "with valid attributes" do
let!(:profile) { create(:profile, user: #user) }
let!(:industry) { create(:industry) }
let!(:attrs) { attributes_for(:product, user_id: #user.id, industry_ids: [ industry.id ]).merge(
product_features_attributes: [attributes_for(:product_feature)],
product_competitions_attributes: [attributes_for(:product_competition)],
product_usecases_attributes: [attributes_for(:product_usecase)]
) }
it "saves the new product in the db" do
expect{ post :create, product: attrs }.to change{ Product.count }.by(1)
end
#If I don't use this the 2 tests below fail. If I use it, then the test above fails.
# before do
# post :create, product: attrs
# end
it { is_expected.to redirect_to product_path(Product.last) }
it { is_expected.to set_flash.to('Product got created!') }
end
end
end
factories
factory :product, class: Product do
#name { Faker::Commerce.product_name }
sequence(:name) { |n| "ABC_#{n}" }
company { Faker::Company.name }
website { 'https://example.com' }
oneliner { Faker::Lorem.sentence }
description { Faker::Lorem.paragraph }
user
end
You can't have it both ways. If you execute the method you are testing in the before, then you can't execute it again to see if it changes the Product count. If you don't execute it in your before, then you must execute it in your example and therefore can't use the is_expected one liner format.
There are a variety of alternatives. Here is one that incorporates the execution of the method into all the examples.
describe "when user logged in" do
before(:each) do
login_user #logged in user is available by calling #user
end
describe "POST create" do
subject(:create) { post :create, product: attrs }
context "with valid attributes" do
let!(:profile) { create(:profile, user: #user) }
let!(:industry) { create(:industry) }
let!(:attrs) { attributes_for(:product, user_id: #user.id, industry_ids: [ industry.id ]).merge(
product_features_attributes: [attributes_for(:product_feature)],
product_competitions_attributes: [attributes_for(:product_competition)],
product_usecases_attributes: [attributes_for(:product_usecase)]
) }
it "saves the new product in the db" do
expect{ create }.to change{ Product.count }.by(1)
end
it("redirects") { expect(create).to redirect_to product_path(Product.last) }
it("flashes") { expect(create).to set_flash.to('Product got created!') }
end
end
end

undefined method `response' for nil:NilClass rspec test

I created a test, and for some reason the should is run on a nil type.
I am using rails 4.2 and rspec-rails 3.1.0. I am not sure what I am doing wrong - this is the test, and the error is on the last it { should respond_with 401 } test
require 'rails_helper'
class Authentication
include Authenticable
def request
end
def response
end
end
describe Authenticable do
let(:authentication) { Authentication.new }
describe "#current_user" do
before do
#user = FactoryGirl.create :user
request.headers["Authorization"] = #user.auth_token
allow(authentication).to receive(:request).and_return(request)
end
it "returns the user from the authorization header" do
expect(authentication.current_user.auth_token).to eql #user.auth_token
end
end
describe "#authenticate_with_token" do
before do
#user = FactoryGirl.create :user
allow(authentication).to receive(:current_user).and_return(nil)
allow(response).to receive(:response_code).and_return(401)
allow(response).to receive(:body).and_return({"errors" => "Not authenticated"}.to_json)
allow(authentication).to receive(:response).and_return(response)
end
it "render a json error message" do
expect(json_response[:errors]).to eql "Not authenticated"
end
it { should respond_with 401 }
end
end
it { should respond_with 401 } does not specify which object should repond with 401, that's why the error.
To fix it try:
expect(response).to respond_with 401
or
use subject:
subject{ response }
it { should respond_with 401 }
subject { authentication }
you should put this line as below
let(:authentication) { Authentication.new }
subject { authentication }
describe '#current_user' do

Where to put mocks

I'm trying to spec this action.
def get
#asset = current_user.assets.find(params[:id])
send_file #asset.uploaded_file.path, type: #asset.uploaded_file_content_type
rescue ActionController::MissingFile
redirect_to assets_url, error: 'missing file'
end
To test the send file method we mock it out.
controller.should_receive(:send_file)
However, I have no idea where to put this mock:
Here's how my spec looks:
subject { response }
let!(:user) { FactoryGirl.create(:user) }
let!(:user_2) { FactoryGirl.create(:user) }
let!(:asset) { FactoryGirl.create(:asset, user_id: user.id) }
let!(:file) { fixture_file_upload('files/eve.jpg', 'image/jpeg') }
let!(:folder) { FactoryGirl.create(:folder, user_id: user.id, parent_id: nil) }
before do
sign_in user
end
describe '#get' do
context 'when exists' do
before do
get :get, id: asset.id
end
# controller.should_receive(:send_file).with(*args) <-- I need to test that
it { should have_http_status 302 }
end
context 'when doesn\'t exist' do
before do
get :get, id: 765
end
it { should redirect_to_location '/assets'}
it { should set_flash_type_to :error }
it { should set_flash_message_to 'missing file' }
end
end
How do I test line 6. I want to keep the one line syntax if possible.
Put it in the before block
before do
controller.should_receive(:send_file)
get :get, id: asset.id
end

Resources