Following the RailsTutorial, I keep getting this error message (one of four actually) when it should be working:
UsersController authentication of edit/update pages for signed-in
users should require matching users for 'edit'
Failure/Error: #users = [#user, second, third] undefined local variable
or method 'second' for
#<RSpec::Core::ExampleGroup::Nested_3::Nested_7::Nested_2:0x000001040dcb68> #
./spec/controllers/users_controller_spec.rb:275:in 'block
(4 levels) in <top (required)>'
I have tried to fix it by looking at other questions on stack overflow but to no avail. My gemfile now looks like this:
gem 'rails', '3.0.3'
gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
gem 'gravatar_image_tag', '0.1.0'
gem 'will_paginate', '3.0.pre2'
group :development do
gem 'rspec-rails', '2.0.1'
gem 'annotate-models', '1.0.4'
gem 'faker', '0.3.1'
end
group :test do
gem 'rspec-rails', '2.0.1'
gem 'webrat', '0.7.1'
gem 'spork', '0.8.4'
gem 'factory_girl_rails', '1.0'
end
Any suggestions would be greatly appreciated!
The backtrace is coming from the users_controller_spec. Without the source, it's hard to say, but I'm guessing it's something like this:
describe UsersController
before do
#user = Factory(:user)
sign_in(#user)
end
context "GET to edit for another user" do
before do
#other_user = Factory(:user)
get :edit, :id => #other_user
end
it "should disallow editing of other users" do
# ... expectations here of HTTP 401 or a user-friendly message
end
end
end
Note that the variables shared between #before blocks and the examples are instance variables - local variables wouldn't be shared across them. I'm guessing your code might be trying to share a local between examples, or between a #before block and its examples.
I'm guessing second and third don't exist when you're calling [#user, second, third] (well, more than guessing. Its what the error says ;) ). They're probably in the tutorial to suggest you could be handling multiple objects in this code. Try removing them.
If that's not it, can you post a link to the tutorial you're following, or provide more background about where second and third are supposed to be coming from?
Related
I'm just getting started with Ruby on Rails and I already feel like an idiot being stuck on something that seems so simple.
I'm stuck on Chapter 7.3.4 of Michael Hartl's Ruby on Rails tutorial. I'm doing an Integration Test on a user signup to check for invalid form submission.
I've thoroughly followed the tutorial so far and have been able to grasp every concept or error I ran into, but this one got me stuck. Upon trying Rails t in the Console (Ubuntu), I'm getting the following error :
ERROR["test_invalid_signup_information", #<Minitest::Reporters::Suite:0x000055b0dec5f190 #name="UsersSignupTest">, 1.8036143100000004]
test_invalid_signup_information#UsersSignupTest (1.80s)
ArgumentError: ArgumentError: wrong number of arguments (given 2, expected 1)
test/integration/users_signup_test.rb:8:in `block (2 levels) in <class:UsersSignupTest>'
test/integration/users_signup_test.rb:7:in `block in <class:UsersSignupTest>'
19/19: [=================================] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.84116s
19 tests, 38 assertions, 0 failures, 1 errors, 0 skips
Here is the test file itself, where the error comes from :
require "test_helper"
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
end
end
Here is the User controller file :
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
# Handle a successful save.
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
And here is my Gemfile :
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '3.0.0'
gem 'rails', '6.1.0'
gem 'bcrypt', '3.1.13'
gem 'bootstrap-sass', '3.4.1'
gem 'puma', '5.0.4'
gem 'sass-rails', '6.0.0'
gem 'webpacker', '4.2.2'
gem 'turbolinks', '5.2.1'
gem 'jbuilder', '2.10.0'
gem 'rexml'
gem 'bootsnap', '1.4.6', require: false
group :development, :test do
gem 'sqlite3', '1.4.2'
gem 'byebug', '11.1.3', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
gem 'web-console', '4.1.0'
gem 'listen', '3.4.1'
gem 'spring', '2.1.1'
gem 'spring-watcher-listen', '2.0.1'
end
group :test do
gem 'capybara', '3.32.2'
gem 'selenium-webdriver', '3.142.7'
gem 'webdrivers', '4.3.0'
gem 'rails-controller-testing', '1.0.4'
gem 'minitest', '5.11.3'
gem 'minitest-reporters', '1.3.8'
gem 'guard', '2.16.2'
gem 'guard-minitest', '2.4.6'
gem 'pry'
end
group :production do
gem 'pg', '1.2.3'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
# Uncomment the following line if you're running Rails
# on a native Windows system:
# gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
I've searched for similar threads and actually found this on StackOverflow :
Chapter 7 Errors, Ruby on Rails tutorial
I do think it's the syntax of the test that must be changed, and I've tried the solution suggested on there, but the test still fails tells me about a wrong number of arguments. It must be pretty simple, although I'm just not seeing it...
Thank you guys in advance for your time and help !
I was receiving and was baffled by the same error. It says that on the line with the TestCase method post I was passing two arguments, while it expected just one. I'm not sure why, since the documentation for that method indicates that it does indeed take two arguments - an action and an args hash.
Anyway, I decided to just follow the logic of the error and write it so that it's only one argument being passed to post - I removed the comma between users_path and params:
assert_no_difference 'User.count' do
post users_path params: { user: { name: "", email: "user#invalid", password: "foo", password_confirmation: "bar" } }
end
I have no idea why that works, especially as it seems to be wrong according to the documentation. But the test now passes without errors.
I have a conditional in my view based upon a helper method defined in my application controller, which I defined as a helper method.
<% if logged_in? %>
When my spec hits this, it says:
ActionView::Template::Error:
undefined method `logged_in?' for #<#<Class:0x007fdc06b71aa8>:0x007fdc0b9b0930>
When I use my browser, it works perfect. I tried stubbing it, but I get another error when I try to stub it on the view or controller. This is what my test looks like:
it "has a logout link" do
render
expect(rendered).to have_link "Logout"
end
Here's my gemfile:
ruby "2.4.1"
gem "bootsnap", ">= 1.1.0", require: false
gem "coffee-rails", "~> 4.2"
gem "jbuilder", "~> 2.5"
gem "pg"
gem "puma", "~> 3.11"
gem "rails", "~> 5.2.1"
gem "rails-controller-testing"
gem "sass-rails", "~> 5.0"
gem "turbolinks", "~> 5"
gem "uglifier", ">= 1.3.0"
group :development, :test do
gem "byebug", platforms: %i[mri mingw x64_mingw]
end
group :development do
gem "listen", ">= 3.0.5", "< 3.2"
gem "spring"
gem "spring-watcher-listen", "~> 2.0.0"
gem "web-console", ">= 3.3.0"
end
group :test do
gem "capybara", ">= 2.15"
gem "chromedriver-helper"
gem "factory_bot_rails"
gem "rspec-rails", "~> 3.7"
gem "selenium-webdriver"
gem "shoulda-matchers", "~> 3.1"
end
Update to the accepted answer, it now seems necessary to use without_partial_double_verification to get around the ActionView::Base does not implement: field_name error.
Example code:
let(:logged_in) { true }
before(:each) do
without_partial_double_verification do
allow(view).to receive(:logged_in?).and_return(logged_in)
end
render
end
it 'has a logout link' do
expect(rendered).to have_link 'Logout'
end
context 'with no logged user' do
let(:logged_in) { false }
it 'has a login link' do
expect(rendered).to have_link 'Login'
end
end
View specs usually does not need to call the real helper (if you wan't to test the helper then you can do a helper spec).
It's faster and easier to just stub the helper method to return the value you want for your given test:
context 'with a logged user' do
before(:each) do
allow(view).to receive(:logged_in?).and_return(true)
end
it 'has a logout link' do
render
expect(rendered).to have_link 'Logout'
end
end
context 'with no logged user' do
before(:each) do
allow(view).to receive(:logged_in?).and_return(false)
end
it 'has a login link' do
render
expect(rendered).to have_link 'Login'
end
end
When you do model, controller, views, helper or any other isolated test, it does not have access to all the app since it's designed to run fast. For example: controller tests does not render views by default, model specs only tests the model, view specs do not have a context of the request nor session, etc.
You can have full real user tests using integration (feature) specs which runs the whole user interaction from beginning to end with access to everything, even controlling what the user clicks.
I have run through mhartls's rails tutorial a few times with rails 4.x and just for fun, I started again, but updating to rails 5.0.0.beta1.
I have managed to find solution to all of the incompatibilities up to chapter 9. Specifically, I can't get the test of the destroy action on the users controller to work. I get:
ERROR["test_should_redirect_destroy_when_not_logged_in", UsersControllerTest, 1.489256506320089]
test_should_redirect_destroy_when_not_logged_in#UsersControllerTest (1.49s)
URI::InvalidURIError: URI::InvalidURIError: bad URI(is not URI?): http://www.example.com:80destroy
test/controllers/users_controller_test.rb:49:in `block (2 levels) in <class:UsersControllerTest>'
test/controllers/users_controller_test.rb:48:in `block in <class:UsersControllerTest>'
URI::InvalidURIError: bad URI(is not URI?): http://www.example.com:80destroy
test/controllers/users_controller_test.rb:49:in `block (2 levels) in <class:UsersControllerTest>'
test/controllers/users_controller_test.rb:48:in `block in <class:UsersControllerTest>'
bin/rails test test/controllers/users_controller_test.rb:47
my controller destroy action:
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
My test :
test "should redirect destroy when logged in as a non-admin" do
log_in_as(#other_user, integration_test: 'true')
assert_no_difference 'User.count' do
delete :destroy, params: { id: #user }
end
assert_redirected_to root_url
end
The links actually work, but test fails. I can get the test to pass by adding a route like this: delete 'delete_user' => 'users#destroy' AND changing the test to: delete delete_user_path params:{id: #user} however, the actual site and the test are using different routes, so I don't know if I can trust this. If I remove destroy from the users resources: resources :users, except: :destroy then the page link doesn't work but the test still passes. I don't see how to use the delete_user route on the page link and cant seem to get the user#destroy route to work in the test. I am aware that this is not an integration test, but this workaround fixed several other problems. I will refactor later, so that it makes sense.
Additional related controller code:
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
Test Helper Code:
# Logs in a test user. (I think all tests must be like integration)
def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
# the next line is the work around for rails version >=5.1
integration_test = options[:integration_test] || 'false'
if integration_test == 'true'
post login_path, params: { session: { email: user.email,
password: password,
remember_me: remember_me }
}
else
# params[:session][:user_id] = user.id
# session[:user_id] = user.id
controller.session[:user_id] = user.id
# params[session[:user_id] ] = user.id
end
end
Gemfile:
source 'https://rubygems.org'
ruby '2.3.0'
gem 'rails', '>= 5.0.0.beta1'
gem 'railties', '>= 5.0.0.beta1'
gem 'bcrypt', '>= 3.1.10'
gem 'faker', '>= 1.6.1'
gem 'carrierwave', '>= 0.10.0'
gem 'mini_magick', '>= 4.3.6'
gem 'fog', '>= 2.0.0.pre.0'
gem 'kaminari', :git => "git://github.com/amatsuda/kaminari.git", :branch => 'master'
gem 'kaminari-bootstrap', '>= 3.0.1'
gem 'bootstrap-sass', '>= 3.3.6'
gem 'sass-rails', '>= 5.0.4'
gem 'uglifier', '>= 2.7.2'
gem 'coffee-rails', '>= 4.1.1'
gem 'jquery-rails', '>= 4.0.5'
gem 'therubyracer', '>= 0.12.2'
gem 'turbolinks', github: 'rails/turbolinks'
gem 'jbuilder', '>= 2.4'
gem 'sdoc', '>= 0.4.1', group: :doc
gem 'net-ssh'
group :development, :test do
gem 'sqlite3', '>= 1.3.11'
gem 'byebug', '>= 8.2.1'
gem 'spring', '>= 1.6.1'
end
group :development do
gem 'web-console', '>= 3.0.0'
end
group :test do
gem 'minitest-reporters', '>= 1.1.7'
gem 'mini_backtrace', '>= 0.1.3'
gem 'guard', '>= 2.13.0'
gem 'guard-minitest', '>= 2.4.4'
gem 'minitest', '>= 5.8.3'
gem 'rails-controller-testing', '>= 0.0.3'
end
group :production do
gem 'pg', '>= 0.18.4'
gem 'rails_12factor', '>= 0.0.3'
gem 'puma', '>= 2.15.3'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', '>= 1.2015.7', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
The correct solution is to replace:
delete :destroy, params: { id: #user }
with:
delete user_url(#user)
It seems that my in current setup ( see gemfile etc.) tokens ( :destroy , :user, etc) are not handled the same as in my previous setup (like mhartl recommends) so the fix I use is to replace all of the tokens in my tests with named routes (:user becomes user_path(#user). The destroy action doesn't seem to have a default named route so I added one to the routes.rb file.
I can get the test to pass by adding a route like this:
delete 'delete_user' => 'users#destroy'
AND changing the test to:
delete delete_user_path params:{id: #user}
however, the actual site and the test are using different routes to the same action, so I don't know if I can trust this as a final solution.
If i remove destroy from the users resources:
resources :users, except: :destroy
then the page link doesn't work but the test still passes. I don't see how to use the added 'delete_user(#user) route' on the page link, I keep getting user not found when I try, and I cant seem to get the user#destroy action to work in the test without the added route.
require 'spec_helper'
describe StudentsController do
fixtures :all
context "#profile" do
let(:school) { Factory.create(:school_2) }
before do
session[:email] = user.email
session[:user_id] = user.id
end
it "should assign the user to #user" do
User.stub(:find).with(user.id).and_return(user)
get :profile
assigns[:user].should eql(user)
end
end
end
An error occurred in an after hook:
NoMethodError: undefined method `original_path_set' for nil:NilClass
occurred at /home/qbadmin/.rvm/gems/ruby-1.9.3-p194/gems/rspec-rails-2.13.1/lib/rspec/rails/view_rendering.rb:121:in `block (2 levels) in <module:ViewRendering>'
This is the error I got when running rspec spec from the command line. Can anyone help me to figure out what the problem is?.
The RSpec versions I have specified in the Gemfile are:
gem 'rspec', '~> 2.13',
gem 'rspec-rails', '~> 2.13'
Thanks in advance.
In your gemfile you should have something like this in order for rspec to work correctly
group :test, :development do
gem 'rspec-rails', '~> 2.13.2'
end
I also recommand you take a look at the database_cleaner gem
group :test do
gem 'database_cleaner', '~> 1.0.1'
end
Regarding your issue, after little research, it seems there is an issue with how rspec is getting the path of your spec file. Can you confirm that the above specs are written in the following file ?
spec/controllers/students_controller_spec.rb
Could you also copy / paste your controllers code please ?
I had this exact issue to day.
It turned out I had forgotten to do a rake db:test:prepare after a database change, before running the test suite again.
I've tried calling both logger.debug and Rails.logger.debug but nothing is getting outputted to the development.log file. Any ideas as to what's going?
Update
I should also note that I'm calling this from a controller and that other information such as the SQL queries are getting outputted to the log file.
Source Code
https://github.com/kyledecot/skateparks-web
Gemfile
source 'http://rubygems.org/'
gem 'rails', '3.0.7'
gem 'rake', '~> 0.8.7'
gem 'devise'
gem 'omniauth'
gem 'flash_cookie_session'
gem 'carrierwave'
gem 'mini_magick'
gem 'barometer'
gem 'gdata_19', :require => 'gdata'
gem 'google_places'
gem 'fleakr'
gem 'resque'
gem 'nokogiri'
gem 'decent_exposure'
gem 'cancan'
gem 'friendly_id'
gem 'breadcrumbs_on_rails'
gem 'defensio'
gem 'twitter'
gem 'thinking-sphinx', :require => 'thinking_sphinx'
gem 'ts-resque-delta', '1.0.0', :require => 'thinking_sphinx/deltas/resque_delta'
gem 'mime-types', :require => 'mime/types'
gem 'vpim'
gem 'will_paginate', '~> 3.0.pre2'
gem 'acts_as_geocodable'
gem 'acts_as_audited', '2.0.0.rc7'
gem 'passenger'
gem 'paper_trail'
gem 'thin'
gem 'compass', '>= 0.11.1'
gem 'guard-compass'
gem 'redcarpet'
gem 'mysql2', '0.2.7'
gem 'placemaker'
group :development, :test do
gem 'sqlite3'
gem 'capybara'
gem 'launchy'
gem "rspec-rails"
gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i
gem 'guard-rspec'
gem 'guard-bundler'
gem 'guard-delayed'
gem 'factory_girl'
gem 'rspec'
end
group :production do
end
enviornment.rb
require File.expand_path('../application', __FILE__)
Skateparks::Application.initialize!
ActiveRecord::Base.logger.level = Logger::INFO
ImagesController
def server
logger.warn "WARN!" # nothing
logger.info "INFO!" # nothing
logger.debug "DEBUG!" # nothing
puts Rails.logger.inspect # shows
puts "PUTS!" # shows
end
Log Output w/ rails s
#<ActiveSupport::BufferedLogger:0x000001058867a0 #level=0, #buffer={}, #auto_flushing=1, #guard=#<Mutex:0x000001058866b0>, #log=#<File:/Users/Kyle/Desktop/skateparks-web/log/development.log>>
PUTS!
Started GET "/images/fd3b38315c30532b3a55bb84d35e5925/34_m.png" for 127.0.0.1 at 2011-06-29 03:41:50 -0400
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
UPDATE:
Viget Labs has removed the loggable gem from their repo (
https://github.com/vigetlabs/loggable
)
Some gem in your app is using the vigetlabs/loggable gem.
Looking at Gemfile.lock, it appears that the "fleakr" gem is using it.
I noted that while logger.info "hello" does not work, Rails.logger.info "hello" works just fine.
It appears the loggable gem is somehow redefining the logger methods to do nothing using the LoggerStub:
class LoggerStub
[:debug, :error, :fatal, :info, :warn].each do |method|
class_eval <<-CODE
def #{method}(message); nil; end
CODE
end
end
What is happening is that the loggable gem is defining a class variable and instance variable called logger, and the .info etc methods are being called on one of them. And those methods simply return nil.
I know this is a partial answer (in that it is not a complete solution), but that should be
good information to get you started on finding the proper solution.
Take a look also at these files:
log_methods.rb
logger_stub.rb
UPDATE
Adding..
class ActionController::Base
self.class.logger = Rails.logger
end
..should forcibly override loggable's assignment of the logger and things should work normally. I suppose the same would work for class ActiveRecord::Base.
But of course, this is a brute force solution that does not fix the underlying problem. For a real "fix" you would need to delve further into fleakr or loggable and maybe ask about this problem at the issues page for those gems.
UPDATE
See if you can make the fleakr gem use the loggable fork from mediashelf : that fork should use the default Rails logger if it is available.
If you're calling :GET "/images/92131721fcf3b8beb12b6e55014b9976/41_m.png"
...isn't that going to be going to the :show action in the images controller, rather than the :index?
(of course, I can't know for sure without knowing your routes file...)