I'm trying to implement an rspec functional test to check that a user is signed in with devise. I've looked through my code again and again and tried different solutions but so far nothing has worked.
I've included what I believe are the relevant files. Let me know if anything is missing. Any recommendations are appreciated.
spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
# Add additional requires below this line. Rails is not loaded until this point!
require 'spec_helper'
require 'rspec/rails'
# note: require 'devise' after require 'rspec/rails'
require 'devise'
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Checks for pending migration and applies them before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
#Added for Devise
config.include Devise::Test::ControllerHelpers, :type => :controller
end
spec/controllers/users_controller_spec.rb
require 'rails_helper'
describe UsersController, :type => :controller do
before do
#user = User.create!(email: 'example#example.com', password: 'example')
end
# let(:user) { User.create!(email: 'example#example.com', password: 'example') }
describe 'GET #show' do
context "when user is logged in" do
before do
sign_in #user
end
it "loads correct user details" do
get :show, id: #user.id
expect(response).to have_http_status(200)
expect(assigns(:user)).to eq #user
end
end
context "when no user is logged in" do
it "redirects to login" do
get :show, id: #user.id
expect(response).to redirect_to(root_path)
end
end
end
end
And my feedback from the terminal after running bundle exec rpsec
Failures:
1) UsersController GET #show when user is logged in loads correct user details
Failure/Error: elsif user?
NoMethodError:
undefined method `user?' for #<Ability:0x007fa1d784cb98>
# ./app/models/ability.rb:9:in `initialize'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:361:in `new'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:361:in `current_ability'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:342:in `authorize!'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:49:in `authorize_resource'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:34:in `load_and_authorize_resource'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:10:in `block in add_before_action'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/rails-controller-testing-1.0.1/lib/rails/controller/testing/template_assertions.rb:61:in `process'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:33:in `block in process'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:100:in `catch'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:100:in `_catch_warden'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:33:in `process'
# /Users/Admin/.rvm/gems/ruby-2.3.0/gems/rails-controller-testing-1.0.1/lib/rails/controller/testing/integration.rb:12:in `block (2 levels) in <module:Integration>'
# ./spec/controllers/users_controller_spec.rb:19:in `block (4 levels) in <top (required)>'
2) UsersController GET #show when no user is logged in redirects to login
Failure/Error: expect(response).to redirect_to(root_path)
Expected response to be a redirect to <http://test.host/> but was a redirect to <http://test.host/users/sign_in>.
Expected "http://test.host/" to be === "http://test.host/users/sign_in".
# ./spec/controllers/users_controller_spec.rb:29:in `block (4 levels) in <top (required)>'
Finished in 0.90214 seconds (files took 11.05 seconds to load)
6 examples, 2 failures
Failed examples:
rspec ./spec/controllers/users_controller_spec.rb:18 # UsersController GET #show when user is logged in loads correct user details
rspec ./spec/controllers/users_controller_spec.rb:27 # UsersController GET #show when no user is logged in redirects to login
A friend actually helped me figure it out. Apparently there's some issue that involves the seperate gem cancancan. I had to go into the models/ability.rb file and change a line in it.
In the original code the line if !user.admin? was just user?
So if you run into this same issue, that's the fix!
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new #guest user (not logged in)
# can :manage, User, id: user.id
if user.admin?
can :manage, :all
elsif !user.admin?
can :manage, User, id: user.id
else
can :read, :all
end
end
end
Related
I am just getting started on rspec, and I may have included to much in the first go. I followed this guide but cant seem to get it all to come together.
I have a Model called Photo, so I have created a Factory girl for that:
# spec/factories/photo.rb
FactoryGirl.define do
factory :photo do
date_taken { Faker::Date.backward(14) }
end
end
I use this in a test:
#spec/models/photo_spec.rb
require 'rails_helper'
RSpec.describe Photo, type: :model do
it "has a valid factory" do
expect(photo).to be_valid
end
end
But I get the following, indicating that the photo factory is not loaded...
rspec spec/models/photo_spec.rb
Failures:
1) Photo has a valid factory
Failure/Error: expect(photo).to be_valid
NameError:
undefined local variable or method `photo' for #<RSpec::ExampleGroups::Photo:0x00000004b769f8>
# ./spec/models/photo_spec.rb:5:in `block (2 levels) in <top (required)>'
Finished in 0.29491 seconds (files took 7.67 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/models/photo_spec.rb:4 # Photo has a valid factory
My config is:
#spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
require 'shoulda/matchers'
require 'database_cleaner'
require 'support/factory_girl'
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Checks for pending migration and applies them before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
in support dir:
#spec/support/factory_girl.rb
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
#spec/support/database_cleaner.rb`enter code here`
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
Down in the comments section on the linked guide, there's a comment by Rhys Davis on 11 Jan 2016
Although in the Model Specs section I had to add the following to
rails_helper.rb: config.include FactoryGirl::Syntax::Methods
then also add the following to my contact_spec.rb: contact =
build(:contact)
and then a reply by Arkadiusz Zdanowski on 5 Sep
Yes, without those lines I got the following error:
Failures:
1) Contact has a valid factory
Failure/Error: expect(contact).to be_valid
NameError:
undefined local variable or method `contact' for #
Which is the same error you're getting now, so adding those 2 lines should probably fix the issue for you.
I am getting the same behavior whether it's in the browser or running rspec... How can this issue get resolved?
I have tried removing the before_filter :authenticate_user! within application_controller.rb and the result remains the same. The failing example is the very first one within spec/features/subdomain_feature_spec.rb
Failures:
1) subdomains redirects invalid accounts
Failure/Error: visit root_url(subdomain: 'random-subdomain')
Capybara::InfiniteRedirectError:
redirected more than 5 times, check for infinite redirects.
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/rack_test/browser.rb:41:in `process_and_follow_redirects'
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/rack_test/browser.rb:22:in `visit'
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/rack_test/driver.rb:43:in `visit'
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/session.rb:254:in `visit'
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/subdomain_feature_spec.rb:7:in `block (2 levels) in <top (required)>'
...
Failed examples:
rspec ./spec/features/subdomain_feature_spec.rb:6 # subdomains redirects invalid accounts
Relevant Files
spec/features/subdomain_feature_spec.rb
require 'rails_helper'
describe 'subdomains' do
let!(:account) { create(:account_with_schema) }
it 'redirects invalid accounts' do
visit root_url(subdomain: 'random-subdomain')
expect(page.current_url).to_not include('random-subdomain')
end
it 'allows valid accounts' do
visit root_url(subdomain: account.subdomain)
expect(page.current_url).to include(account.subdomain)
end
it 'forces user to login before accessing subdomain content' do
visit root_url(subdomain: account.subdomain)
expect(page).to have_content 'sign in or sign up before continuing'
end
end
config/routes.rb
class SubdomainPresent
def self.matches?(request)
request.subdomain.present?
end
end
class SubdomainBlank
def self.matches?(request)
request.subdomain.blank?
end
end
Rails.application.routes.draw do
constraints(SubdomainPresent) do
root 'projects#index', as: :subdomain_root
devise_for :users
end
# only allow certain routes when there isn't a subdomain
constraints(SubdomainBlank) do
root 'welcome#index'
resources :accounts, only: [:new, :create]
end
end
controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :load_schema, :authenticate_user!
private
def load_schema
Apartment::Tenant.switch!('public')
return unless request.subdomain.present?
account = Account.find_by(subdomain: request.subdomain)
if account
Apartment::Tenant.switch!(request.subdomain)
else
redirect_to root_url(subdomain: :false)
end
end
def after_sign_out_path_for(resource_or_scope)
new_user_session_path
end
end
controllers/welcome_controller.rb
class WelcomeController < ApplicationController
skip_before_filter :authenticate_user!, only: :index
def index
end
end
spec/factories/accounts.rb
FactoryGirl.define do
sequence(:subdomain) { |n| "subdomain#{n}" }
factory :account do
sequence(:subdomain) { |n| "subdomain#{n}" }
association :owner, factory: :user
factory :account_with_schema do
after(:build) do |account|
Apartment::Tenant.create(account.subdomain)
Apartment::Tenant.switch!(account.subdomain)
end
after(:create) do |account|
Apartment::Tenant.reset
end
end
end
end
spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
# Add additional requires below this line. Rails is not loaded until this point!
require 'spec_helper'
require 'rspec/rails'
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
config.include FactoryGirl::Syntax::Methods
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end
# Capybara.register_driver :poltergeist do |app|
# Capybara::Poltergeist::Driver.new(app, options)
# end
The Capybara rack_test driver ignores hostnames so you're not going to be able to test subdomain behavior with it, you'll need to run those tests with one of the drivers that utilizes/mimics a full browser (selenium, poltergeist, capybara-webkit).
Additionally you want to write and url/path check using the Capybara have_current_path matcher rather than eq with `current_url'
expect(page).not_to have_current_path(/random-subdomain/, url: true)
I added cancancan gem to my app, but it broked all my controller and feature tests. The error start in ability.rb initialize: the user variable is nil.
Here is the error log of one of the tests:
14) TestCasesController DELETE #destroy when not logged-in redirects to sign-in page
Failure/Error: if user.teacher?
NoMethodError:
undefined method `teacher?' for nil:NilClass
# ./app/models/ability.rb:5:in `initialize'
# /usr/local/rvm/gems/ruby-2.3.1/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:361:in `new'
# /usr/local/rvm/gems/ruby-2.3.1/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:361:in `current_ability'
# /usr/local/rvm/gems/ruby-2.3.1/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:342:in `authorize!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:49:in `authorize_resource'
# /usr/local/rvm/gems/ruby-2.3.1/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:34:in `load_and_authorize_resource'
# /usr/local/rvm/gems/ruby-2.3.1/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:10:in `block in add_before_action'
# /usr/local/rvm/gems/ruby-2.3.1/gems/rails-controller-testing-1.0.1/lib/rails/controller/testing/template_assertions.rb:61:in `process'
# /usr/local/rvm/gems/ruby-2.3.1/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:33:in `block in process'
# /usr/local/rvm/gems/ruby-2.3.1/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:100:in `catch'
# /usr/local/rvm/gems/ruby-2.3.1/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:100:in `_catch_warden'
# /usr/local/rvm/gems/ruby-2.3.1/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:33:in `process'
# /usr/local/rvm/gems/ruby-2.3.1/gems/rails-controller-testing-1.0.1/lib/rails/controller/testing/integration.rb:12:in `block (2 levels) in <module:Integration>'
# ./spec/controllers/test_cases_controller_spec.rb:182:in `block (3 levels) in <top (required)>'
# ./spec/controllers/test_cases_controller_spec.rb:193:in `block (4 levels) in <top (required)>'
Here is the corresponding action:
describe "DELETE #destroy" do
let!(:test_case) { create(:test_case) }
subject { delete :destroy, params: { id: test_case } }
context "when not logged-in" do
before { subject }
it "redirects to sign-in page" do
expect(response).to redirect_to(new_user_session_url)
end
end
end
This is my controller:
class TestCasesController < ApplicationController
include ApplicationHelper
load_and_authorize_resource
before_action :authenticate_user!
before_action :find_test_case, only: [:show, :edit, :update, :destroy, :test]
before_action :find_question, only: [:index, :new, :create, :test_all]
def index
#test_cases = TestCase.where(question: #question)
end
def new
#test_case = TestCase.new
end
def create
#test_case = #question.test_cases.build(test_case_params)
if #test_case.save
flash[:success] = "Caso de teste criado!"
redirect_to #test_case
else
render 'new'
end
end
def show
end
def edit
end
def update
if #test_case.update_attributes(test_case_params)
flash[:success] = "Caso de teste atualizado!"
redirect_to #test_case
else
render 'edit'
end
end
def destroy
question = #test_case.question
#test_case.destroy
flash[:success] = "Caso de teste deletado!"
redirect_to question_test_cases_url(question)
end
def test
result = #test_case.test(plain_current_datetime, "pas", params[:source_code])
#output = result[:output]
#results = [ { title: #test_case.title, status: result[:status] } ]
respond_to { |format| format.js }
end
def test_all
#results = #question.test_all(plain_current_datetime, "pas", params[:source_code])
respond_to { |format| format.js }
end
private
def test_case_params
params.require(:test_case).permit(:title, :description, :input, :output, :question_id)
end
def find_test_case
#test_case = TestCase.find(params[:id])
end
def find_question
#question = Question.find(params[:question_id])
end
end
This is my ability class:
class Ability
include CanCan::Ability
def initialize(user)
if user.teacher?
can :manage, [Exercise, Question, TestCase]
can :manage, Team, owner: user
can [:read, :create], Team
can [:enroll], Team do |team|
!team.enrolled?(user)
end
can [:unenroll], Team do |team|
team.enrolled?(user)
end
end
end
end
This is my rails_helper (from rspec):
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
require 'support/helpers/relationships'
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Checks for pending migration and applies them before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
# Helpers
config.include Helpers::Relationships, type: :model
config.include Devise::Test::ControllerHelpers, :type => :controller
config.include Warden::Test::Helpers
end
Any tips of how to fix that?
OBS: I'm using Devise for authentication, it provides me the required current_user to cancancan work.
The primary reason you're getting a nil:NilClass error is because current_user is nil unless you are signed in via Devise. Your ability needs to handle for when a user is not logged in, something like this:
def initialize(user)
return unless user.present?
if user.teacher?
...
end
end
If you return nil from your ability, the authorization fails and is handled accordingly.
It's with a capybara feature test. I have no before_action filters set in my controller for the root page, so I'm completely stumped by this. Anyone else had the same problem?
The line that causes the error is simply
visit(root_path)
Very weird.
Also, when running just this test on its own, it passes, but when running the whole test suite, it fails with the InfiniteRedirectError.
user_and_role_spec.rb:
require 'rails_helper'
def manually_create_user
visit new_user_registration_path
fill_in('user_first_name', :with => 'Test')
fill_in('user_last_name', :with => 'User')
fill_in('user_email', :with => 'testuser#email.com')
fill_in('user_password', :with => 'testuser')
fill_in('user_password_confirmation', :with => 'testuser')
click_button('Sign up')
end
def create_user_and_login_as(type)
user = FactoryGirl.create(type)
visit(new_user_session_path)
fill_in('user_email', :with => user.email)
fill_in('user_password', :with => user.password)
click_button('Log in')
end
describe 'with users and roles' do
context "if user is not an admin" do
it "makes sure Login/Logout works" do
visit(root_path)
click_link("Sign up")
fill_in('user_email', :with => "testuser#email.com")
fill_in('user_first_name', :with => "Test")
fill_in('user_last_name', :with => "User")
fill_in('user_password', :with => "password")
fill_in('user_password_confirmation', :with => "password")
click_button "Sign up"
expect(current_path).to eq(root_path)
expect(page).to have_content('Welcome! You have signed up successfully.')
end
end
end
static_pages_controller.rb:
class StaticPagesController < ApplicationController
before_action :an_admin?, only: [:admin]
def home
#testimonials = Testimonial.all
end
def admin
#groups = Group.all
#users = User.all
#students = Student.all
#teachers = Teacher.all
end
private
def an_admin?
unless signed_in? && (current_user.admin == true)
redirect_to root_path, notice: "You have to be a signed-in admin to view the admin page"
end
end
end
routes.rb:
Rails.application.routes.draw do
resources :materials
root 'static_pages#home'
devise_for :users, :controllers => { registrations: 'registrations' }
get 'admin' => 'static_pages#admin'
resources :groups
resources :users
resources :students
resources :teachers
resources :testimonials
post 'assign_to_group' => 'students#assign_to_group' # Could have been 'patch', but default in the controller method is 'post', so I left the method as default and changed this route to 'post'. Doesn't NEED to be patch.
post 'remove_from_group' => 'students#remove_from_group'
post 'unassign_teacher' => 'groups#unassign_teacher'
post 'assign_as_student' => 'teachers#assign_as_student'
post 'assign_as_teacher' => 'students#assign_as_teacher'
post 'add_student' => 'groups#add_student'
post 'remove_student_from_group' => 'groups#remove_student_from_group'
end
rails_helper.rb:
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require 'spec_helper'
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'
require 'capybara/rspec'
require 'database_cleaner'
require 'devise'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
config.include Devise::TestHelpers, :type => :controller
Capybara.register_driver :rack_test do |app|
Capybara::RackTest::Driver.new(app, :respect_data_method => true, :redirect_limit => 20)
end
end
spec_helper.rb:
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
end
test.log:
Started GET "/" for 127.0.0.1 at 2015-05-19 13:37:49 +0200
Processing by StaticPagesController#home as HTML
Redirected to http://www.example.com/
Filter chain halted as :an_admin? rendered or redirected
Completed 302 Found in 6ms (ActiveRecord: 0.0ms)
Started GET "/" for 127.0.0.1 at 2015-05-19 13:37:49 +0200
Processing by StaticPagesController#home as HTML
Redirected to http://www.example.com/
Filter chain halted as :an_admin? rendered or redirected
Completed 302 Found in 7ms (ActiveRecord: 0.0ms)
Started GET "/" for 127.0.0.1 at 2015-05-19 13:37:49 +0200
Processing by StaticPagesController#home as HTML
Redirected to http://www.example.com/
Filter chain halted as :an_admin? rendered or redirected
...etc etc
To me, this says my before_action in my static_pages_controller kicked in, but I don't understand why it would, given the an_admin? method's code?
UPDATE:
I may be getting close to solving this: static_controller_spec is run before user_and_role_spec when running the full test suite. When I disable static_controller_spec, the user_and_role_spec test runs with no errors when running the whole test suite. The culprit seems to be this line:
controller.class.skip_before_action :an_admin?
This line is in the static_controller_spec:
require "rails_helper.rb"
describe StaticPagesController do
describe "GET #home" do
it "renders the :home view" do
get :home
expect(response).to render_template :home
end
end
describe "GET #admin" do
it "renders the :admin view" do
# This is line 14. The next line is intended to disable the :an_admin? before_action in the controller
controller.class.skip_before_action :an_admin?
get :admin
expect(response).to render_template :admin
# The next line is intended to reverse line 15
controller.class.before_action :an_admin?
end
it "requires user to be signed_in"
it "requires user to be an admin"
end
end
I wanted to disable the before_action for this test. To be honest, I don't really understand that line - it was a straight copy/paste from somewhere. It seems to be messing up my user_and_role_spec test, though, but I don't understand why. Any ideas?
In your user_and_role_spec.rb you have an extra end after the end closing def create_user_and_login_as(type).
You need two end at the bottom of spec to close describe 'with users and roles' do and context "if user is not an admin" do.
In your routes.rb you need an end at the bottom to close Rails.application.routes.draw do
I am not sure if you just did not paste them, but it does matter in your application and it does matter when you post code.
SOLVED:
With reference to the update in my original post, I simply completely removed those two controller.class lines in the static_pages_controller_spec and the whole test suite passes (thank god!). Of course, it does make me wonder why I had those lines in there in the first place - I simply can't remember.
It's all still a bit of a mystery, though, which is why I started this other thread to try to shed some light on it. I know I'm asking for a fair bit of hand-holding here, but any help would be much appreciated. I do study a lot myself, but it's also great to get other people's input too.
I'm trying to run the $ rspec command but all my controller tests are failing unless I explictly add :type => :controller to each of the specs.
Here's the error I'm getting:
1) AccountsController GET index assigns all accounts as #accounts
Failure/Error: get :index, {}, valid_session
NoMethodError:
undefined method `get' for #<RSpec::ExampleGroups::AccountsController_2::GETIndex:0x007fd96c8a6a68>
# ./spec/controllers/accounts_controller_spec.rb:36:in `block (3 levels) in <top (required)>'
Here's the generated spec:
require 'spec_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
describe AccountsController do
# This should return the minimal set of attributes required to create a valid
# Account. As you add validations to Account, be sure to
# adjust the attributes here as well.
let(:valid_attributes) { { "subdomain" => "MyString" } }
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# AccountsController. Be sure to keep this updated too.
let(:valid_session) { {} }
describe "GET index" do
it "assigns all accounts as #accounts" do
account = Account.create! valid_attributes
get :index, {}, valid_session
assigns(:accounts).should eq([account])
end
end
...
If I look in the spec/rails_helper.rb file, I see this snippet:
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
But I'm new to RSpec and TDD and i'm not sure if this rails_helper.rb file is being read or not.
spec/spec_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'email_spec'
require 'rspec/autorun'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include(EmailSpec::Helpers)
config.include(EmailSpec::Matchers)
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
#config.order = "random"
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
Can anyone tell me how I can get my controller specs to run without having to explicitly add :type => :controller? The controller specs are all in the dir spec/controllers so RSpec should just know that they are controller specs.
You're running RSpec 3 and you haven't fully upgraded your configuration from RSpec 2.
rspec-rails 3 generates 'spec_helper.rb' and 'rails_helper.rb'. 'rails_helper.rb' requires 'spec_helper.rb'. In rspec-rails 3, specs of Rails classes should require 'rails_helper', not require 'spec_helper' as the generated spec is doing. So, either
change require 'spec_helper' to require 'rails_helper',
regenerate your scaffold (I believe your spec was generated by rspec-rails 2), or
remove the require altogether and put --require rails_helper in your project's .rspec file so it is automatically included in all specs.
And then config.infer_spec_type_from_file_location! will take effect.