I am currently trying to write unit tests for user authentication in rails and I keep running into a problem.
I am trying to test the following method:
def reset_session_token!
self.session_token = User.generate_session_token
self.save!
self.session_token
end
with the following unit test:
let(:valid_user) { User.new(user_name: 'Name', password: 'abcdefghijkl')}
it "should set the session_token" do
valid_user.reset_session_token!
expect(valid_user.session_token).not_to be_nil
end
but the test fails with the error Validation failed: User name has already been taken. I suspect that it is because reset_session_token! calls save on the user instance but this is necessary for the method to work properly. How can I get around this?
Thanks to Oleg Sobchuk's comment I was able to figure out how to deal with the save! method.
Since stub has been deprecated I used allow_any_instance_of with receive instead. Here is what I did:
it "should set the session_token" do
allow_any_instance_of(User).to receive(:save!).and_return(nil)
valid_user.reset_session_token!
expect(valid_user.session_token).not_to be_nil
end
Related
We currently face a problem when a request spec fails (typo or other exception) it affects other request specs which actually should be unaffected. The problem is we use many gems so I tried to create a minimal reproduction app (without success, https://github.com/tak1n/reproduction)
The problem in detail:
We have a request spec where we need to login a user. For user authentication we use devise and therefore we use Warden::Test::Helpers to log in users in request specs. Devise automatically updates some user related attributes when a user logs in (last_sign_in_at, last_sign_in_ip etc..).
The problem here is that when it tries to save the user for doing this changes the end result is a INSERT INTO user instead of a UPDATE user and therefore it crashes.
Here is an example spec from our app:
require 'rails_helper'
RSpec.describe 'Suggestions API' do
let(:user) { FactoryGirl.create(:user, :professional) }
before do
login(user) # same as in https://github.com/tak1n/reproduction/blob/master/spec/support/request_macros.rb#L6
end
describe '/suggestions.json', :vcr do
context 'with non saved filter' do
# some setup stuff here (setup proper objects in db)
# also params are defined here through let(:params) { ... }
it 'returns proper suggestions' do
Suggestion.refresh!
get '/suggestions.json', paramst
expect(json.count).to eq(2)
expect(json.first['id']).to eq(sug2.id)
expect(json.second['id']).to eq(sug1.id)
end
end
context 'with saved filter' do
# some setup stuff here (setup proper objects in db)
# also params are defined here through let(:params) { ... }
it 'returns proper suggestions' do
Suggestion.refresh!
get '/suggestions.json', params
expect(json.count).to eq(2)
expect(json.first['id']).to eq(sug2.id)
expect(json.second['id']).to eq(sug1.id)
end
end
end
end
The result of this should be that the first spec fails because of the typo paramst, which it does but it also affects the second spec:
Here is the spec run: https://gist.github.com/tak1n/102c1aa121b66e0ab56602b76f911ec0
I tried to dig deeper and saw that the logic whether save creates or updates a record is depending on https://github.com/rails/rails/blob/master/activerecord/lib/active_record/persistence.rb#L85
Next I tried to output what #new_record was in that case.
def new_record?
sync_with_transaction_state
puts "New record: #{#new_record}" if self.class == User
#new_record
end
I got following: https://gist.github.com/tak1n/330560a3a108abc8fce4d105a48ac444
When the specs run in different order (non failing first then spec with typo or exception in it) I got this: https://gist.github.com/tak1n/6eb24693226d8e6a713c0865ea1bebd5
The difference here is for the "working" spec run New Record: <boolean> is different. When the spec with the exception runs before it New record suddenly becomes true and therefore it generates SQL to create a new record.
Next I assumed some gems are causing this problem, most likely I thought it would be database_cleaner but in the reproduction I do more or less the same and there it works.
The problem is I'm really stuck at debugging and not sure where to proceed. Also I'm not sure which gem or our code itself is causing this.
Feel free to ask any questions if you need further details, thanks in advance.
Seems like the issue is caused by devise-async (https://github.com/mhfs/devise-async).
See https://github.com/rails/rails/issues/26038 and https://github.com/mhfs/devise-async/issues/96 for more information.
In my Rails application I have a User model:
class User
def self.foo
User.all.each{ |user| user.bar }
end
def bar
end
end
In my spec file I want to check that foo calls bar for every user, so far that's what I have:
describe '::foo' do
let!(:users) { Fabricate.times(5, :user) }
it 'calls bar for every user' do
users.each do |user|
expect(user).to receive(:bar)
end
User.foo
end
end
Although the method gets called (I debugged it, so I'm sure of that) the spec is red.
Also I tried to write this code to understand where the problem was:
let!(:user) { Fabricate(:user) }
it 'fails' do
expect(user).to receive(:bar)
User.first.bar
end
it 'pass' do
expect(user).to receive(:bar)
user.bar
end
It seems that if I reference my instance directly it works, if I obtain it from the DB the expectation doesn't work.
I use mongoid, not sure if this is relevant.
I believe it cannot be done due to how RSpec works: When you set an expectation, RSpec essentially 'wraps' the object so that it can keep track of the messages it receives.
But when the implementation code fetches records from the database, they are not wrapped, so RSpec isn't able to record their messages.
RSpec does have a method allow_any_instance_of which can help in some cases, but its use is discouraged, and don't think it would be suitable here.
In this situation, I would suggest stubbing User.all to return some doubles (two should be sufficient). You can then verify that bar is called on each one.
I have an application running in rails 4.1 using mongoid as the orm. I created a model called User which has an attribute email. I am using RSpec for tests. I created the following spec
require 'spec_helper'
describe 'User' do
before(:each) do
#attr = {
user: {
email: "rahul#gmail.com"
}
}
end
it "should create a valid User instance" do
param = ActionController::Parameters.new(#attr)
param.require(:user).permit!
User.create!(param)
end
end
when I run the spec, I get the following error
Failure/Error: User.create!(param)
ActiveModel::ForbiddenAttributesError:
ActiveModel::ForbiddenAttributesError
I know this is related to strong parameters but couldn't figure out what I am doing wrong.
From the fine manual:
require(key)
[...] returns the parameter at the given key [...]
So saying param.require(:user) does nothing at all to param, it merely does an existence check and returns param[:user].
I think you want to say something more like this:
param = ActionController::Parameters.new(#attr)
User.create!(param.require(:user).permit!)
That usage would match the usual:
def some_controller_method
#user = User.create(user_param)
end
def user_param
param.require(:user).permit!
end
usage in controllers.
I have a still pretty simple Rails application that I want to develop using BDD with Cucumber and TDD with RSpec. Currently, I am hanging at a test where I want to check that if a new instance of an Organizer (that's the model I have) cannot be created due to a validation error. I would like to check that the errors Array of the object to be created is not empty so that I can be sure that error messages are available for showing them in the view.
require 'spec_helper'
describe OrganizersController do
render_views
describe "POST 'create'" do
describe "with invalid arguments" do
before(:each) do
request.env["HTTP_REFERER"] = organizers_new_path
#organizer_args = { :name => "" }
end
it "should return a non-empty list of errors" do
post 'create', :organizer => #organizer_args
#organizer.errors.empty?.should_not be_true
end
end
end
end
I am developing based on Rails 3.2.9 with RSpec 2 and cucumber-rails.
Any suggestions are appreciated. Thanks!
You should use assigns method to get instance variable from controller action:
assigns(:organizer).errors.empty?.should_not be_true
The latest preferred syntax is:
expect(assigns(:organizer).errors.empty?).to_not be_true
thanks for the answer guys but I'd like to suggest a slightly nicer syntax:
expect(assigns(:organizer).errors).to_not be_empty
(unrelated to the question 👇)
Basically whenever you have a method that ends with ? you'll have the corresponding rspec matcher that starts with be_ e.g.
1.odd? #=> true
expect(1).to be_odd
I've been struggling with creating a login function that should be executed before any rspec test is run.
What I have right now is:
def login
post "/session", { "session[username]" => "bjones" }
end
in my spec_helper.rb file
Then, I have the following in one of my spec.rb files in the requests directory.
require 'spec_helper'
describe "Sessions" do
describe "GET /dashboards" do
login
it "displays the dashboard" do
get dashboard_path
puts response.body
end
end
end
However, when I try running the test, I get:
undefined method `post' for #<Class:0x4f08828> (NoMethodError)
I'm pretty new to rails and completely new to testing and rspec so maybe there's something fundamental I'm missing here. Basically, all I want to do is set that session variable so that when the test is run I will be logged in. Perhaps a different approach would be better? Or maybe I need to put that login function in a different place?
I came across this answer which was sort of useful but it's not for rspec so I'm having trouble understanding where such a helper function would go.
Try
let(:login) {
post "/session", { "username" => "bjones" }.to_json
}
This might have to be revised to use .to_json or not, depending on what content type the controller accepts.