Rails 4 Rspec test session when assigned in spec - ruby-on-rails-4

In my test i assign session to a specific value but when i run the test and print from my app controller, the session is nil. Any idea how should i do this ?
Here is my spec :
RSpec.describe "welcome/index.html.haml", type: :view do
before(:each) do
session[:followed_repos] = ["rails/rails"]
end
context "when visitor tracked repositories" do
it "should see tracked repository list" do
visit root_path
expect(page).to have_no_content("You are tracking 0 repositories. Search for more here")
expect(page).to render_template(:partial => "_tracked_repo")
end
end
end
Here is my controller where i print the value :
def index
puts session[:followed_repos].inspect
end
Thx for your help.

I found 2 answers that might help people trying to use session in type: :view :
You can use that gem: rack_session_access
You have no other choice then reproduce all the needed steps to get the session you need

Related

ruby rails test case failing but real app works

So here's a strange problem: When I start my local rails app and browse to http://localhost:3000/static_pages/help I can see the page I created there.
However, the test case that I wrote says otherwise.
static_pages_controller_test.rb
require 'test_helper'
class StaticPagesControllerTest < ActionController::TestCase
test "should get home" do
get :home
assert_response :success
end
test "should get help" do
puts static_pages_help_url
puts static_pages_help_path
get static_pages_help_url
assert_response :success
end
end
It fails with this error, Output of $bin/rake test:
Running:
..http://test.host/static_pages/help
/static_pages/help
E
Finished in 0.466745s, 8.5700 runs/s, 4.2850 assertions/s.
1) Error.
StaticPagesControllerTest#test_should_get_help:
ActionController::UrlGenerationError: No route matches {:action=>"http://test.host/static_pages/help", :controller=>"static_pages"}
test/controllers/static_pages_controller_test.rb:12:in `block in <class:StaticPagesControllerTest>'
Here is routes.rb
Rails.application.routes.draw do
get 'static_pages/home'
get "static_pages/help"
end
and here is the static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
end
def help
end
end
and these two files
app/views/static_pages/home.html.erb
app/views/static_pages/help.html.erb
exist, as I can also see them when navigating to /static_pages/help in my browser. I've searched the web for hours, no clue.
$ rails --version
Rails 4.2.7.1
$ ruby --version
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
I must be missing something. Please help.
Since you're writing a controller spec, the parameter to a GET should be the action(controller method). But you're passing a URL. If you look at the error message, you can find that "http://test.host/static_pages/help" was passed into action. So, pass the name of the controller method as a symbol rather than the URL. Try
get :help
Note that help is the controller action.
However if you're interested in writing an integration test, you should inherit from ActionDispatch::IntegrationTest rather than ActionController::TestCase. So, your spec should look aomething like this.
class StaticPagesControllerTest < ActionDispatch::IntegrationTest
test "should get home" do
get static_pages_home_url
assert_response :success
end
test "should get help" do
get static_pages_help_url
assert_response :success
end
end
To learn more about integration and controller tests, see http://weblog.jamisbuck.org/2007/1/30/unit-vs-functional-vs-integration.html
Hope this helps!

Ruby on Rails Pundit's current_user is nil in integration test

I'm using the gems pundit and devise. I have a delete link that only shows up if you are an admin. I have an integration test that I would like to verify that the delete link only shows up for admins.
test 'comment delete link shows when it should' do
log_in_as #admin
get movie_path(#movie)
assert_select 'a[href=?]', movie_comment_path(comments(:one), #movie.id)
end
My test_helper.rb looks like this:
...
class ActiveSupport::TestCase
...
def log_in_as(user, options = {})
password = options[:password] || 'password'
if integration_test?
post user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
else
Devise::TestHelpers.sign_in user
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
The response.body looks all right, but indeed there is no delete link. There is one when I run the development server and visit the page myself. I've narrowed this down to the current_user that pundit uses in the policies is being passed in with a value of nil. This is my comment_policy.rb:
class CommentPolicy
attr_reader :current_user, :comment
def initialize(current_user, model)
#current_user = current_user
#comment = model
end
def create?
if #current_user
#current_user.member? or #current_user.content_creator? or #current_user.moderator? or #current_user.admin?
end
end
def destroy?
if #current_user
#current_user == #comment.user or #current_user.moderator? or #current_user.admin?
end
end
end
As a closing remark, I've heard that Rails 5 has opted for integration tests instead of controller tests as we know them from Rails 4 for the default type of tests to be generated for our controllers. If this is the case, devise would be a heck of a lot more useful out of the box when using Rails 5 if the sign_in/sign_out helpers that work in controller tests were made to work in integration tests as well. But would I still have this issue of pundit not knowing what current_user is? I'm assuming this all works fine in controller tests because the current_user is scoped to controllers? Any and all light shed on this topic is much appreciated, but I would really like to figure out how to get integration tests to work with this setup because I have about a billion I want to write right now.
Not that it totally matters, but does it need to be using current_user in the policy or can it just use user in the policy. By this I mean according to the elabs/pundit README on Github I would just use #user and user everywhere instead of current_user. Read the README if I confused you.
Additionally the nil for current_user typically occurs when you don't have a valid CSRF token for your request. When you do this on the website manually by going to localhost:3000 or w/e you are first performing a get on the login path before doing the post on the login path with your credentials. In your integration test I don't seem to see where you are performing that get in order to get the CSRF for your session.
Hope this helps!!!

"undefined method `env' for nil:NilClass" in 'setup_controller_for_warden' error when testing Devise using Rspec

I'm trying to create a spec for a sign out flow by using factorygirl to create a user and then use Devise's sign_in method to authenticate the user, then use capybara to click the "Sign Out" link.
I'm getting (what seems to me to be) a strange error when I run the spec:
Failures:
1) Sign out flow successfully redirects to the welcome index (root)
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `env' for nil:NilClass
# /home/vagrant/.rvm/gems/ruby-2.0.0-p576/gems/devise-3.4.1/lib/devise/test_helpers.rb:24:in `setup_controller_for_warden'
Finished in 0.00226 seconds (files took 3.32 seconds to load)
1 example, 1 failure
Here's the spec:
require 'rails_helper'
describe "Sign out flow" do
include Devise::TestHelpers
describe "successfully" do
it "redirects to the welcome index (root)" do
user = create(:user)
sign_in user
within '.user-info' do
click_link 'Sign Out'
end
expect(current_path).to eq root_path
end
end
end
And my user.rb factory:
FactoryGirl.define do
factory :user do
name "Fake User"
sequence(:email, 100) { |n| "person#{n}#example.com" }
password "helloworld"
password_confirmation "helloworld"
confirmed_at Time.now
end
end
The error seems to be triggered simply from the line include Devise::TestHelpers, as I've tried commenting out the entire content of the spec and still get the same error.
I thought the Devise test helpers would work out of the box; did I miss some configuration? Thanks.
In Rails 5 you must include Devise::Test::IntegrationHelpers instead Devise::Test::ControllerHelpers:
# rails_helper.rb
config.include Devise::Test::IntegrationHelpers, type: :feature
See more:
https://github.com/plataformatec/devise/issues/3913#issuecomment
https://github.com/plataformatec/devise/pull/4071
Apparently there are issues with Devise::TestHelpers and integration testing, so perhaps that's the problem here.
https://github.com/plataformatec/devise (mentioned in README, Issues, etc.; also see related SO questions):
These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session;
FWIW it seems like the issues have been fixed, however I ran into the issue after not reading the documentation well enough.
This was our code:
RSpec.configure do |config|
...
config.include Devise::TestHelpers
...
end
This means every test will include the test helpers, including models. This wound up being the issue for us. Should we have read the documentation closer we would have noticed Devise suggests limiting it to only controllers with:
RSpec.configure do |config|
...
config.include Devise::TestHelpers, type: :controller
...
end
This solved the issue for us. All tests passing :)
Here's my solution:
class ActiveSupport::TestCase
# all the normal stuff
end
class ActionController::TestCase
include Devise::TestHelpers
end
I meet the same error on rails 5.
Here's my solution
spec/rails_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
config.include Devise::TestHelpers, type: :view
config.include Warden::Test::Helpers
end
spec/controllers/your_controller_spec.rb
RSpec.describe YourController, type: :controller do
before(:all) do
user = FactoryGirl.create(:user)
login_as user, scope: :user
end
it "#index" do
get "index"
expect(response).to render_template(:index)
expect(response).to have_http_status(200)
end
$ rspec --tag focus
Run options: include {:focus=>true}
DashboardController
#index
Finished in 3.9 seconds (files took 3.5 seconds to load)
1 example, 0 failures
Like others have already said, you're including the Devise::TestHelpers. That's for testing controllers. If you'd still like to automatically login a test user in your integration tests, check out the official Devise Instructions on using it with Capybara.
Using Devise with Capybara
Basically, what you need to do is first enable Warden's test mode:
include Warden::Test::Helpers
Warden.test_mode!
Then, (create and) login your user:
user = FactoryGirl.create(:user)
login_as(user, scope: :user)
Example:
# spec/features/survey_spec.rb
require 'rails_helper'
feature 'survey app' do
include Warden::Test::Helpers
let(:user) { create(:user) }
let(:survey) { create(:survey_with_questions) }
before do
# Sign the User in
Warden.test_mode!
login_as(user, scope: user)
end
it 'renders the survey' do
visit survey_show_path(survey)
expect(page).to have_content(survey.title)
end
end
I was having this problem when trying to sign_in a user in a before hook:
before(:context) do
create(:skill, name: 'Google Maps API'.downcase)
user = create(:user)
sign_in user
end
Placing sign_in inside the before hook leads to:
Failure/Error: sign_in user
NoMethodError:
undefined method `env' for nil:NilClass
But placing it inside an example works fine:
shared_examples_for('an authenticated resource.') do
describe 'An authenticated request' do
it "responds with HTTP status OK" do
user = create(:user)
sign_in user
make_request
expect(response).to have_http_status(:ok)
end
end
end
But this can be improved, placing the sign_in into a before(:example) that will also work:
context 'allow search by keyword' do
let!(:skill){ create(:skill, name: 'Google Maps API'.downcase) }
let!(:user) { create(:user) }
before(:example) { sign_in user }
it 'finds matching records' do
get :search, name: "Google Maps API", format: :json
expect(assigns(:skills).size).to be(1)
end
it 'finds records that start with keyword'
it 'finds records that end with keyword'
it 'finds records that contains keyword'
end
My Devise version is 4.2.0 so I just included
config.include Devise::Test::ControllerHelpers, type: :controller
in my rails helper file.
Alternatively you can use the same in your spec as
include Devise::Test::ControllerHelpers
The correct syntax for Rails 5 / Devise (4.2.0) is
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, :type => :controller
end
Devise::TestHelpers are deprecated so use Devise::Test::ControllerHelpers
:type => :controller - to limit only for
controllers and not models for example.
For the sake of being complete, with Rails 5 and RSpec I have run into similar issues when using the latest helpers, as they need to be set explicitly with the type when not used as a superclass.
So if you find yourself receiving there errors in your model tests there's a pretty good chance the type is not set.
Here's what I use in the spec_helper:
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :feature
I know that the docs do mention this, but there are times when you may run across an older blog that gives you an older approach, or upgrading from an older setup, and next thing you know this happens.

Getting tripped up by verify_partial_doubles with rails 4 and rspec 3

I'm using authlogic for my user authentication and in my ApplicationController I have "current_user", "current_user_session", etc. defined and set as helper_methods.
I have an extremely simple view spec for my main index:
RSpec.describe "main/index.html.erb", :type => :view do
context "when not logged in" do
before do
allow(view).to receive(:current_user).and_return(nil)
end
it "has an h1" do
render
expect(rendered).to include('h1')
end
end
end
The problem is that if "mocks.verify_partial_doubles = true" in my config then this causes an impressively massive error as it dumps an entire object and then says at the bottom:
1) main/index.html.erb when not logged in has an h1
Failure/Error: allow(view).to receive(:current_user).and_return(nil)
#<#<Class:0x00000104c249d0>:.........
#rendered_views={}>> does not implement: current_user
Of course, it is recommended that verify_partial_doubles is set to true, but in doing so this breaks. I pulled this straight from the documentation:
https://www.relishapp.com/rspec/rspec-rails/v/3-1/docs/view-specs/view-spec#passing-view-spec-that-stubs-a-helper-method
If the method appears in ApplicationHelper it'll work. But if it's in ApplicationController and defined as a helper_method there's no such luck:
helper_method :current_user, ...
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
I want the protection that verify_partial_doubles provides, how can I work around this?
This is a known issue and the only way to get it working is to extract the methods into a module and include it in your view helpers and the controller.
More information at: https://github.com/rspec/rspec-rails/issues/1076
You can disable double verification for views as follows:
RSpec.configure do |config|
config.before(:each, type: :view) do
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = false
end
end
config.after(:each, type: :view) do
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
end
This way you'll be able to keep stubbing view methods with:
allow(view).to receive(:current_user).and_return(nil)
More information at: https://github.com/rspec/rspec-rails/issues/1076

How to write a functional test case in rails 3.1 and unit:test

i am new to rails testing and i am using unit:test. I have an action in my controller
def save_campaign
unless params[:app_id].blank?
#app = TestApp.find(params[:app_id])
if params[:test_app]
#app.update_attributes(params[:test_app])
end
flash[:notice] = "Your Registration Process is completed"
redirect_to "/dashboard"
else
redirect_to root_path
end
end
and my test case is as following
test "should save campagin " do
assert_difference('TestApp.count', 0) do
post :save_campaign, test_app: #test_app.attributes
end
assert_redirected_to "/dashboard"
end
end
This method is a post method. While running this test, it is failing and showing me a message
"should save campagin (0.07s)
Expected response to be a redirect to http://test.host/dashboard but was a redirect to http://test.host/
/home/nouman/.rvm/gems/ruby-1.9.2-p290#global/gems/actionpack-3.1.3/lib/action_dispatch/testing/assertions/response.rb:67:in `assert_redirected_to'
My guess is that i am not giving it right assertion to check params
params[:app_id] and #app = TestApp.find(params[:app_id]).
How can i write such an assertion to check these attributes, check wether a parameter is blank. How can 1 find an object with a given id.
For functional test, you should not care about testing the model, that is in your case, you should remove:
assert_difference('TestApp.count', 0) do
..
end
What you want to know in a functional test is that if the page is loaded, redirected correctly.
In your controller, you have a condition check for params, so for each of the outcome of the check, you write a test each, that is you have to write two functional tests:
test "if app_id param is empty, #save_campaign redirect to root" do
post :save_campaign, :app_id => nil
assert_redirected_to root_path
end
test "#save_campaign" do
post :save_campaign, :app_id => app_fixture_id, :test_app => #test_app.attributes.to_params
assert_redirected_to '/dashboard'
end
The trick to prepare the post params is to use method to_params method.
Hope this help.
UPDATE: If you just want to check if params[:app_id] GET param is in the URL, you should just check for this presence instead of checking if it is blank or not:
if params[:app_id]
else
end