I've got a really old Rails 2.3.18, ruby 1.9.3, rspec 1.x application which we are upgrading and it had restful-authentication in it. So I've replaced that with Devise 1.0.11.
I can login to the application, but my tests will not run;
Here is the test in question
require 'spec_helper'
describe CategoriesController do
context "As a logged in user" do
before do
login_user
current_firm = mock_model(Firm, :id => 1)
controller.stub!(:current_firm).and_return(current_firm)
end
describe "#index" do
it "should render index" do
get :index
response.should render_template('index')
end
end
end
end
Here is the error I get;
NoMethodError in 'CategoriesController As a logged in user#index should render index'
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]=
/home/map7/code/pdfcat/spec/spec_helper.rb:18:in `login_user'
spec/controllers/categories_controller_spec.rb:6:in `block (3 levels) in <top (required)>'
The error happens on this line;
[20, 29] in /usr/local/rbenv/versions/1.9.3-p551/lib/ruby/gems/1.9.1/gems/warden-0.10.7/lib/warden/session_serializer.rb
20 key
21 end
22
23 def store(user, scope)
24 return unless user
=> 25 session[key_for(scope)] = serialize(user)
26 end
The problem is 'session' is nil when I'm at this point.
I've pushed the full code to here: https://github.com/map7/pdfcat/tree/devise
My plan was to get devise working in the tests then I could jump to Rails 3.0 and continue the upgrade.
There's an old message in google groups that I think is relevant: https://groups.google.com/forum/#!topic/rspec/4AHuPtHFD34
It recommends using this:
before do
request.env['warden'].stub(:authenticate!) { double(User) }
end
I'd probably put it in rails_helper.rb so that it runs for all tests
Related
I have a user which I'm creating with FactoryGirl which needs to have a company in order to login successfully to my root_url.
I'm not having any luck at all stubbing the user method to login. I've followed this tutorial for the Devise portion of the user and needed to amend it a little since my user also requires a company to be associated to it.
I've now created a new model/controller called Scans that is behind Devise's authenticate filter and my first pass at testing it failing with:
5) ScansController GET #show returns http success
Failure/Error: expect(response).to have_http_status(:success)
expected the response to have a success status code (2xx) but it was 302
# ./spec/controllers/scans_controller_spec.rb:32:in `block (3 levels) in <top (required)>'
# ./spec/spec_helper.rb:127:in `block (3 levels) in <top (required)>'
# ./spec/spec_helper.rb:126:in `block (2 levels) in <top (required)>'
The spec is currently:
require 'rails_helper'
RSpec.describe ScansController, type: :controller do
before(:all) do
#user = build(:user)
#company = build(:company)
#device = build(:device)
#scan = build(:scan)
end
describe "GET #show" do
it "returns http success" do
login_with #user
get :show, :device_id => #device.id, :id => #scan.id
expect(response).to render_template(:show)
end
end
I'm doing a puts on the response, because I want to see what's being returned:
ScansController
GET #show
302
{"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1; mode=block", "X-Content-Type-Options"=>"nosniff", "Location"=>"http://test.host/login", "Content-Type"=>"text/html; charset=utf-8"}
#<Rack::BodyProxy:0x007fb52a7407c0>
So, I'm being redirected back to my login page, which tells me that my login_with method in ControllerHelpers is not working correctly:
module ControllerHelpers
def login_with(user = double('user'), scope = :user)
current_user = "current_#{scope}".to_sym
if user.nil?
allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => scope})
allow(controller).to receive(current_user).and_return(nil)
else
allow(request.env['warden']).to receive(:authenticate!).and_return(user)
allow(controller).to receive(current_user).and_return(user)
end
end
end
Now, my login functionality does currently work (testing manually). The first controller that fires after ApplicationController is PagesController#home:
def home
if current_user && current_user.company
verify_subscription
....
else
redirect_to new_company_path
end
end
If verify_subscription fails the user is also sent to new_company_path, so that doesn't seem to be related to this issue.
Based off my rudimentary rspec capabilities, am I right to assume that I'm not even getting close to mimicking a login? If not, what am I doing wrong?
After alot of tinkering I finally got my tests to pass. I ended up creating a company within my user Factory:
after(:build) do |user|
user.company = create(:company)
end
Using RSPEC and DEVISE, I'm able to sign in a user to run my tests. However, I've recently come across a problem with my method because using before :each is causing multiple users to be created, which causes my tests to break.
Should I change my method of testing? Or change my method of signing in a user?
campaign_controller_spec.rb
before :each do
#user = FactoryGirl.create(:user)
sign_in :user, #user
#business = FactoryGirl.create(:business, user: #user)
end
describe "GET #index" do
before :each do
FactoryGirl.create(:active_campaign, business: #business)
FactoryGirl.create(:inactive_campaign, business: #business)
end
it "no status or type filter" do
get :index # <- LINE 22 OF ERROR
expect(assigns(:campaigns).size).to eq 2
end
it 'status filter' do
get :index, status: 'active'
expect(assigns(:campaigns).size).to eq 1
end
end
campaigns_controller.rb
def index
# Get current user's campaigns
#campaigns = current_user.business.campaigns
# Filter by status (constraints are in routes)
#campaigns = #campaigns.send(params[:status]) unless params[:status] == 'all' # <- LINE 7 OF ERROR
end
The problem is that all of the campaigns belong to the business with belong to USER-1 in the spec, but in the controller, the campaigns belong to another user (because several are being created) which doesn't have any campaigns associated.
The exact error is:
1) CampaignsController [Signed in] GET #index no status or type filter
Failure/Error: get :index
TypeError:
nil is not a symbol nor a string
# ./app/controllers/campaigns_controller.rb:7:in `index'
# ./spec/controllers/campaigns_controller_spec.rb:22:in `block (4 levels) in <top (required)>'
Small Update
I DO have the Database Cleaner gem installed, which should clear the DB between tests. Here is the config (just in case it is incorrect):
#DatabaseCleaner
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
When using factory_girl you'll want to use something like the database_cleaner gem to clean your database between tests. Check out:
https://github.com/DatabaseCleaner/database_cleaner#user-content-rspec-example
EDIT:
Realized that your error isn't caused by what you think it's caused. It's because you're doing a .send(params[:status]) when params[:status] is nil because it's not passed in on the call to get :index... so once .send(nil) is invoked you get the stack trace.
I recently upgraded the RSpec version of my Rails 4 app. This is my setup:
# /Gemfile:
source 'https://rubygems.org'
group :development, :test do
gem 'rspec-rails', '~> 2.99'
...
end
# /spec/controllers/companies_controller_spec.rb:
require 'spec_helper'
describe CompaniesController, :type => :controller do
before(:all) do
#user = FactoryGirl.create(:user)
sign_in(#user)
end
describe 'GET #new' do
it "renders the :new view" do
get :new
expect(response).to render_template :new
end
end
...
end
# /spec/support/utilities.rb:
def sign_in(user)
cookies[:remember_token] = user.remember_token
end
The problem is that most of my tests are now failing and I've been trying to fix it for hours, but to no avail.
This is the error I get for nearly all tests that require a user sign in:
Failure/Error: sign_in(#user)
NoMethodError:
undefined method `cookie_jar' for nil:NilClass
# ./spec/support/utilities.rb:2:in `sign_in'
The #user object gets created by FactoryGirl, however. I've checked that in the test database and in the logs.
Why is my method for signing in users not working?
It worked like a charm before upgrading to a later version of RSpec.
Thanks for any help.
In a before(:all) you shouldn't be doing stuff that only makes sense in the context of a single example.
In particular your sign_in method fiddles with the cookies, which is implicitly the current request/controller's cookies. This doesn't make sense in a before(:all) since each example is supposed to have a separate request - the exception occurs because there is no current request. Change the before(:all) to a before(:each) and you should be ok.
Rspec used to be more lenient in this respect, but 2.99 tightened this up, deprecation warnings were also added for similar uses in some of the earlier 2.x versions (if you had depreciations about using let/subject in a before(:all) block, that was it)
I'm wrestling with Harlt's Chapter 9 exercise 9.
When I design my test for this exercise with :no_capybara set to true, like in other sections of the tutorial, the test PASSES, but I get the following warning:
WARNING: ignoring the provided expectation message argument (true) since it is not a string.
This version of the test is here:
*spec/requests/authentication_pages_spec.rb*
describe "as an admin user" do
let(:admin) {FactoryGirl.create(:admin)}
before do
sign_in(admin, :no_capybara => true)
end
describe "should not be able to delete itself by direclty submitting request" do
before { delete user_path(admin) }
specify { response.should redirect_to(users_path),
flash[:error].should =~ /Can't delete own admin account/i }
end
end
Note this this is how Hartl uses that method in other spaces of the tutorial, as follows:
*spec/requests/authentication_pages_spec.rb*
describe 'signed in user' do
let(:user) { FactoryGirl.create(:user) }
before { sign_in user, no_capybara: true }
describe 'unable to access now' do
before {get new_user_path}
specify { expect(response).to redirect_to(root_url)}
end
.
.
.ect...
However, when I do not set :no_capybara, my test fails:
describe "as an admin user" do
let(:admin) {FactoryGirl.create(:admin)}
before { sign_in(admin) }
.
.
.
Failures:
1) Authentication authorization as a non-admin user submitting a DELETE request to the Users#destroy action
Failure/Error: before { delete user_path(user)}
NoMethodError:
undefined method `admin?' for nil:NilClass
# ./app/controllers/users_controller.rb:68:in `admin_user'
# ./spec/requests/authentication_pages_spec.rb:74:in `block (5 levels) in <top (required)>'
My two main questions are:
Why is that warning occurring during that test, but not in other tests where I pass :no_capybara to the sign_in method?
Why is my test failing if I don't pass it no_capybara? I've seen the other questions on stackoverflow related to this exercise, and other people's solutions don't require no_capybara.
Below is all code within my app that might be applicable. If I should include more, please let me know.
*users_controller.rb*
before_action :admin_user, only: :destroy
def destroy
user = User.find(params[:id])
if !current_user?(user)
user.destroy
flash[:success] = "User destroyed. ID: #{user.id}"
else
flash[:error] = "Can't delete own admin account"
end
redirect_to users_path
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
*controllers/helpers/session_helper.rb*
def current_user
remember_token = User.encrypt(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
I think in second case rails couldn't able to find admin? method since its not present.
Thats why you are getting this error
NoMethodError:
undefined method `admin?' for nil:NilClass
did you ran these codes
$ rails generate migration add_admin_to_users admin:boolean
$ bundle exec rake db:migrate
$ bundle exec rake test:prepare
As per your error, i think rails is looking for a method instead of just checking boolean value for admin.
I set up a controller which handles omniauth authentications which are worked into a custom built authentication system. i am trying to test the logic for how authentications are handled (ex: if user already has/does not have account, if user is/isn't currently logged in, etc.). as such i have a Authorization model and a authorizations controller. The action to create a authorization has this general outline:
class AuthorizationsController < ApplicationController
def create
omniauth = request.env['omniauth.auth']
authorization = Authorization.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authorization
# Authorization already established, log in user
elsif current_user
# User is logged in but wants to add another omniauth authentication
else
# Create user and associate them with omniauth authentication
end
end
end
I am trying to test this logic in Rspec but have been having issues. Heres is what I am working with in my spec:
describe AuthorizationsController do
render_views
describe "POST 'create'" do
describe "with an already existing authorization" do
it "should log the user in" do
#authmock = mock_model(Authorization)
Authorization.should_receive(:find_by_provider_and_uid).and_return(#authmock)
post :create, :provider => 'twitter'
current_user?(#authmock.user).should == true
response.should redirect_to(root_path)
end
end
end
end
I am under the impression that this should assign my mocked Authorization model (#authmock) to the local variable authorization in my controller when the assignment call is made, thus making 'if authorization' return true. However whenever I true to run this spec I get this error:
Failures:
1) AuthorizationsController POST 'create' with an already existing authorization should log the user in
Failure/Error: post :create, :provider => 'twitter'
NoMethodError:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
# ./app/controllers/authorizations_controller.rb:5:in `create'
# ./spec/controllers/authorizations_controller_spec.rb:16:in `block (4 levels) in <top (required)>'
Can anyone enlighten me as to what I am doing wrong here?
Edit:
since the question was raised as to whether or not the assignment of omniauth was causing issues, I commented out that line to see what would happen and got the following error:
1) AuthorizationsController POST 'create' with an already existing authorization should log the user in
Failure/Error: post :create, :provider => 'twitter'
NameError:
undefined local variable or method `omniauth' for #<AuthorizationsController:0xb41809c>
# ./app/controllers/authorizations_controller.rb:5:in `create'
# ./spec/controllers/authorizations_controller_spec.rb:16:in `block (4 levels) in <top (required)>'
which tells me that the problem is with the mock or stub as the find_by_provider_and_uid function is still being evaluated and is not stubbed when the test runs
Are you specing
current_user?(#authmock.user).should == true
or
response.should redirect_to(root_path)
I think that first expectation should not be tested here, because you've mocked 'if authorization' block, so you should spec what happens then!