The problem is you need to say should_receive
rather than should receive
. This is because of the any_instance
. User.any_instance.should receive
means that whatever object any_instance
returns (an RSpec::Mocks::AnyInstance::Recorder
) should receive the call. Of course that's not what you want, because that object is also not the same instance as what the controller instantiates. (In fact it's not even a User
.) So the Recorder
has a special should_receive
method that does what you actually want. Tricky!
Rspec controller test - should_receive on instance of model returns 0 times
-
13-10-2022 - |
Вопрос
I'm having an issue with Rails 4.0.3 and rspec 2.14.1 in testing a controller.
The relevant portion of the controller is:
class LoginsController < ApplicationController
def sign_in
@user = User.find_by(email: params[:email])
# ... - a few other codepaths but nothing that looks for primary_phone
if params[:email]
@user.send_token
flash[:notice] = "blah blah"
end
end
User.rb is:
class User < ActiveRecord::Base
# ...
def send_token
raise 'Tried to send token to contact with no phone number' if primary_phone.nil?
SMSSender.sms(primary_phone,"Your login code is: #{generate_token}")
end
end
The spec is:
require 'spec_helper'
describe LoginsController do
it "sends a token if a valid email is provided" do
@u = create(:user, primary_phone: "abc")
User.any_instance.should receive(:send_token)
post 'sign_in', email: @u.email
end
end
And, my user factory:
FactoryGirl.define do
factory :user do
name "MyString"
email "a@b.com"
end
end
When I change the spec's @u = create
line to @u = create(:user)
(ie, omitting the primary_phone
), I get:
Failure/Error: post 'sign_in', email: @u.email
RuntimeError:
Tried to send token to contact with no phone number
# ./app/models/user.rb:16:in `send_token'
# ./app/controllers/logins_controller.rb:19:in `sign_in'
# ./spec/controllers/logins_controller_spec.rb:14:in `block (3 levels) in <top (required)>'
This is as expected. When I change it back to include the primary_phone
, I get:
1) LoginsController sign_in sends a token if a valid email is provided
Failure/Error: User.any_instance.should receive(:send_token)
(#<RSpec::Mocks::AnyInstance::Recorder:0x007ff537ed4bd8>).send_token(any args)
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/controllers/logins_controller_spec.rb:14:in `block (3 levels) in <top (required)>'
Having trouble understanding why that change would prevent the spec from passing. I did attach a debugger right after the 'post' in the spec and looked at the flash to see if it was correct (i.e., to ensure the proper code tree in the controller was being run) and it is.
Решение
Другие советы
The User
object you've created in your spec is not the same User
object that the sign_in
method creates and sends send_token
to, so the expectations you set on @u
as reflected in your error message are not going to be met. They both are associated with the same underlying database record, but they are different Ruby objects. (Note: In the first version of your question, the code you showed for your spec didn't match the error you showed, as the code showed setting an expectation on User.any_instance
whereas your error message reflected setting an expectation on @u
Further, the expectations need to be set prior to the call you are expecting (e.g. prior to the post
in your case, as noted in the comment by @PaulAJungwirth.
Finally, as an alternative to the answer provided by @PaulAJungwirth, you can use:
expect_any_instance_of(User).to receive(:send_token)
to address the problem with the your stated expectation line.