Difference between expect and expect_any_instance_of - ruby-on-rails

I have method in my controller that looks like this:
def resend_confirmation
#current_user.send_confirmation_instructions
render json: nil, status: 200
end
I've written following spec for that method:
require 'rails_helper'
describe 'POST /api/v1/users/:id/resend_confirmation' do
let!(:current_user) { create(:user) }
before do
expect(current_user).to receive(:send_confirmation_instructions)
post resend_confirmation_api_v1_user_path(current_user),
headers: http_authorization_header(current_user)
end
describe 'response' do
it 'is empty' do
expect(response.body).to eq 'null'
end
end
it 'returns 200 http status code' do
expect(response.status).to eq 200
end
end
But the problem is that this spec is not passing. This line is failing:
expect(current_user).to receive(:send_confirmation_instructions)
when I change that to
expect_any_instance_of(User).to receive(:send_confirmation_instructions)
everything works pretty well. Could someone explain me why spec with expect syntax is not passing?
EDIT:
This how error looks:
Failures:
1) POST /api/v1/users/:id/resend_confirmation returns 200 http status code
Failure/Error: expect(current_user).to receive(:send_confirmation_instructions)
(#<User id: 4175, email: "test1#user.com", date_of_birth: "1990-01-01", created_at: "2016-07-18 06:56:52", updated_at: "2016-07-18 06:56:52", sex: "male", touch_id_enabled: false, first_name: "Test", last_name: "User", athena_health_patient_id: nil, photo_url: nil, admin: false, one_signal_player_id: "1", phone_number: nil, state: nil, address: nil, city: nil, zip_code: nil, phone_number_confirmed: false>).send_confirmation_instructions(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/requests/api/v1/user/resend_confirmation_spec.rb:7:in `block (2 levels) in <top (required)>'
2) POST /api/v1/users/:id/resend_confirmation response is empty
Failure/Error: expect(current_user).to receive(:send_confirmation_instructions)
(#<User id: 4176, email: "test2#user.com", date_of_birth: "1990-01-01", created_at: "2016-07-18 06:56:53", updated_at: "2016-07-18 06:56:53", sex: "male", touch_id_enabled: false, first_name: "Test", last_name: "User", athena_health_patient_id: nil, photo_url: nil, admin: false, one_signal_player_id: "2", phone_number: nil, state: nil, address: nil, city: nil, zip_code: nil, phone_number_confirmed: false>).send_confirmation_instructions(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/requests/api/v1/user/resend_confirmation_spec.rb:7:in `block (2 levels) in <top (required)>'

expects(...) sets the expectation on a particular instance. When the POST request is executed, your app will try to identify the user referenced in your request information, and will create an instance that represents it.
However, that instance is not the same you prepared in your test. It indeed references the same user object, but it's not the same Ruby object.
Therefore, inside your tests, the current_user that is used is not the one you set the expectations on.
Using expect_any_instance_of instead affects every instance of user created, hence also the one that will be created to satisfy the request.

Related

RSpec controller GET #index test is returning an empty ActiveRecord array instead of my model

Please excuse my rustiness, first time touching Rails and this project in quite some time.
Ruby Version: 2.5.0
Rails Version: 5.1.7
RSpec Version: 3.9.3
FactoryBot Version: 6.2.0
This is my scripts_controller_spec.rb file with model creation and the test in question:
require 'rails_helper'
describe ScriptsController, type: :controller do
userID_1 = User.create!(
email: 'ueferfrfrf#u1.com',
password: 'useruser',
password_confirmation: 'useruser'
)
script1 = Script.create!(
name: 'YWoodcutter',
skill: 'Woodcutting',
bot_for: 'TRiBot',
game_for: 'Oldschool Runescape 07',
user_id: userID_1.id
)
script1.save
describe "GET #index" do
it "assigns #scripts" do
get :index
p script1
expect(assigns(:scripts)).to eq([script1])
end
end
When running the tests, the print line above outputs this, as expected:
#<Script id: 1, name: "YWoodcutter", skill: "Woodcutting", bot_for: "TRiBot", game_for: "Oldschool Runescape 07", user_id: 1, created_at:
"2021-10-19 08:29:43", updated_at: "2021-10-19 08:29:43">
However, I get this test failure:
Failures:
ScriptsController GET #index assigns #scripts
Failure/Error: expect(assigns(:scripts)).to eq([script1])
expected: [#<Script id: 1, name: "YWoodcutter", skill: "Woodcutting", bot_for: "TRiBot", game_for: "Oldschool Runescape 07",
user_id: 1, created_at: "2021-10-19 08:29:43", updated_at: "2021-10-19
08:29:43">]
 
got: #<ActiveRecord::Relation []>
(compared using ==)
My scripts_controller.rb index function looks like so:
class ScriptsController < ApplicationController
def index
#scripts = Script.order(:created_at)
end
Let me know if you need any more info, and thanks for your help!
I think the Script object is not getting created before calling the index action. Because of this, you are getting the empty ActiveRecord::Relation. In this situation let! should fix your problem
require 'rails_helper'
describe ScriptsController, type: :controller do
let!(:user_1) do
User.create!(
email: 'ueferfrfrf#u1.com',
password: 'useruser',
password_confirmation: 'useruser'
)
end
let!(:script1) do
Script.create!(
name: 'YWoodcutter',
skill: 'Woodcutting',
bot_for: 'TRiBot',
game_for: 'Oldschool Runescape 07',
user_id: user_1.id
)
end
describe "GET #index" do
before { get :index }
it "assigns #scripts" do
expect(assigns(:scripts)).to eq([script1])
end
end
end
Based on the current code, it seems you were not calling all script.
Using
### Controller
#scripts = Script.all.order(:created_at)
### Test
## Should use factories to create the records
let(:user) do
create(:user, email: 'ueferfrfrf#u1.com',
password: 'useruser',
password_confirmation: 'useruser')
end
let(:script) do
create(:script, name: 'YWoodcutter',
skill: 'Woodcutting',
bot_for: 'TRiBot',
game_for: 'Oldschool Runescape 07',
user: user)
end
should fix it.

Testing that Devise is re-sending confirmation instructions email in a background worker

I want to send Devise confirmation instructions to users a second time if they haven't confirmed within two days of signing up, however I can't seem to get my success case test to pass.
Background worker (runs once a day):
class ResendConfirmationWorker
include Sidekiq::Worker
sidekiq_options queue: :resend_confirmation, retry: false
def perform
d = Time.zone.now - 2.days
users = User.where.not(confirmation_sent_at: nil)
.where(confirmed_at: nil)
.where(created_at: d.beginning_of_day..d.end_of_day)
users.find_each do |user|
user.send_confirmation_instructions
end
end
end
RSpec test:
require 'rails_helper'
describe ResendConfirmationWorker, type: :job do
before do
time = Time.zone.now - 2.days
#user = create :unconfirmed_user,
created_at: time,
confirmation_sent_at: time
end
def run_job
subject.perform
end
it 'resends a confirmation email to people who haven’t accepted it within two days' do
run_job
expect(Devise::Mailer.deliveries.count).to eq 1
end
end
I always get 0 instead of 1. I've also tried looking at Sidekiq::Extensions::DelayedMailer.jobs.size and Devise.mailer.deliveries.count but they also return 0.
Lastly, I put a binding in the worker and run user.send_confirmation_instructions manually:
[1] pry(#<ResendConfirmationWorker>)> user.send_confirmation_instructions
=> #<ActionMailer::DeliveryJob:0x007fc53f05c420
#arguments=
["Devise::Mailer",
"confirmation_instructions",
"deliver_now",
#<User id: 1, email: "unconfirmed1#example.com", created_at: "2017-01-05 04:40:13", updated_at: "2017-01-07 04:40:13", company_id: nil, name: "Unconfirmed User", invitation_token: nil, invitation_created_at: nil, invitation_sent_at: nil, invitation_accepted_at: nil, invitation_limit: nil, invited_by_type: nil, invited_by_id: nil, invitations_count: 0>,
"token1",
{}],
#job_id="9b5f6231-3194-4c57-9a6b-a38368cec603",
#priority=nil,
#queue_name="mailers">
It looks like it's correctly adding a new confirmation instructions email to the mailer queue, so why can't I see it from my test?

Testing Rails views in RSpec: why does it route to "show" when I want to test "index"?

I have this very basic view spec in spec/views/users/index_spec.rb:
require 'rails_helper'
RSpec.describe "users/index", type: :view do
before(:each) do
#user = create(:user)
assign(:users, [#user])
end
it "renders a list of users" do
render
expect(page).to have_selector "tr##{dom_id(#user)}"
end
end
When executing it, it's telling me the following:
Failures:
1) users/index renders a list of users
Failure/Error: render
ActionView::Template::Error:
No route matches {:action=>"show", :controller=>"users", :id=>nil, :locale=>#<User id: 1, name: "Rosalinda Dach", email: "marilie#leffler.ca", encrypted_password: "$2a$04$G/z6lbFUpnh9FD3bymYBE.LrJK3acKr4TsURgCq7B77...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, confirmation_token: nil, confirmed_at: "2015-06-03 14:33:11", confirmation_sent_at: nil, unconfirmed_email: nil, failed_attempts: 0, unlock_token: nil, locked_at: nil, created_at: "2015-06-03 14:33:12", updated_at: "2015-06-03 14:33:12", avatar: nil>} missing required keys: [:id]
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/journey/formatter.rb:46:in `generate'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:727:in `generate'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:758:in `generate'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:801:in `url_for'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:280:in `call'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:345:in `block (2 levels) in define_url_helper'
Why does the test route to users#show instead of users#index?
You can change your code:
RSpec.describe "users/index.html.erb", type: :view do
Since I didn't find a suitable solution, I decided to create my own online Markdown editor with copy and paste: PIMP (Pasteable Images, Markdown, Pandoc).
http://pimp.suhail.uberspace.de/en
It's not yet looking very great yet, but it offers everything you need to create semantically awesome documents using Pandoc's very powerful Markdown, paste images directly from clipboard into the text, and export the documents to various formats (at the time being Docx, Epub, Odt, maybe later also PDF).
At the time being, to use PIMP, one has to create a user account.
The whole project is rather a proof of concept than a real project, but if people like and use it, I would be happy to make it a real project.

Using RSpec to test for correct order of records in a model

I'm new to rails and RSpec and would like some pointers on how to get this test to work.
I want emails to be sorted from newest to oldest and I'm having trouble testing this.
I'm new to Rails and so far I'm having a harder time getting my tests to work then the actual functionality.
Updated
require 'spec_helper'
describe Email do
before do
#email = Email.new(email_address: "user#example.com")
end
subject { #email }
it { should respond_to(:email_address) }
it { should respond_to(:newsletter) }
it { should be_valid }
describe "order" do
#email_newest = Email.new(email_address: "newest#example.com")
it "should have the right emails in the right order" do
Email.all.should == [#email_newest, #email]
end
end
end
Here is the error I get:
1) Email order should have the right emails in the right order
Failure/Error: Email.all.should == [#email_newest, #email]
expected: [nil, #<Email id: nil, email_address: "user#example.com", newsletter: nil, created_at: nil, updated_at: nil>]
got: [] (using ==)
Diff:
## -1,3 +1,2 ##
-[nil,
- #<Email id: nil, email_address: "user#example.com", newsletter: nil, created_at: nil, updated_at: nil>]
+[]
# ./spec/models/email_spec.rb:32:in `block (3 levels) in <top (required)>'
In your code:
it "should have the right emails in the right order" do
Email.should == [#email_newest, #email]
end
You are setting the expectation that the Email model should be equal to the array of emails.
Email is a class. You can't just expect the class to be equal to an array. All emails can be found by using all method on class Email.
You must set the expectation for two arrays to be equal.
it "should have the right emails in the right order" do
Email.order('created_at desc').all.should == [#email_newest, #email]
end
It should work like this.
For newer version of RSpec:
let(:emails) { ... }
it 'returns emails in correct order' do
expect(emails).to eq(['1', '2', '3'])
end

Rspec new expectation syntax

I've the following rspec unit test:
require 'spec_helper'
describe Article do
describe ".recents" do
it "includes articles created less than one week ago" do
article = Article.create(created_at: Date.today - 1.week + 1.second)
expect(Article.recents).to eql([article])
end
it "excludes articles published at midnight one week ago" do
article = Article.create!(:created_at => Date.today - 1.week)
expect(Article.recents).to be_empty
end
end
end
and the Articlemodel:
class Article < ActiveRecord::Base
attr_accessible :description, :name, :price, :created_at
scope :recents, where('created_at <= ?', 1.week.ago)
end
when I run my tests I get:
1) Article.recents includes articles created less than one week ago
Failure/Error: expect(Article.recents).to eql([article])
expected: [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]
got: [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]
(compared using eql?)
Diff:#<ActiveRecord::Relation:0x007ff692bce158>.==([#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]) returned false even though the diff between #<ActiveRecord::Relation:0x007ff692bce158> and [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>] is empty. Check the implementation of #<ActiveRecord::Relation:0x007ff692bce158>.==.
# ./spec/models/article_spec.rb:7:in `block (3 levels) in <top (required)>'
Could someone please help me to figure out what's the error in my test?
It seems good for me.
You are comparing an activerecord relation (Article.recents) to an array ([article]), which is why the expectation is failing. (It looks like they are the same in the spec results because inspect converts the relation into an array before printing it out.)
Change your first expectation to this:
expect(Article.recents.to_a).to eql([article])

Resources