Cannot find existing route - ruby-on-rails-4

I have this route
get 'versions' => 'main#versions', formats: [:xml]
In my MainController, I have an action versions thus:
class MainController < ApplicationController
...
def versions
render formats: [:xml]
end
end
and under app/views/main I have versions.xml.erb.
I have written an RSpec controller test:
require 'rails_helper'
RSpec.describe MainController, type: :controller do
...
describe '#versions' do
it 'renders versions template' do
get :versions, formats: [:xml]
expect(response).to render_template(:versions)
end
end
end
However, I continue to get this error:
Failure/Error: get :versions, formats: [:xml]
ActionController::UrlGenerationError:
No route matches {:action=>"versions", :controller=>"main", :formats=>["xml"]}
However, when I run rake routes, I get:
versions GET /versions(.:format) main#versions {:formats=>[:xml]}
I still don't understand why I'm getting this error when the route exists with the right parameters.
Ruby 2.1.5
Rails 4.1.6
RSpec 3.8.x
NOTE: I'm constrained to not rewrite any code and can only suggest changes.

Rails' routing uses a format key, not formats. It's automatically supported for all endpoints by default, and will pass on whatever format is required to your controller actions.
I'd suggest the following modifications:
# routes.rb
get 'versions', to: 'main#versions'
# main_controller.rb
def versions
respond_to do |format|
format.xml
end
end
# main_controller_spec.rb
it 'renders the version template' do
get :versions, format: :xml
...
end

Related

Rails Custom Error Page Working and Not Working

ruby-2.3.7 Rails 5.1.4
I'm trying to learn about error pages in Rails apps: how to create them and how they get called/triggered. I've read quite a few posts and blogs about this. I've found several that use the same approach. It seems simple enough, so I tried to implement it.
When I go directly to my new 404 or 500 page: ...myapp/404 or ...myapp/500 I get the correct custom 404/500 page. But when I trigger a different 500 error, I do not. I.e: ...myapp.employee/99999 throws an error because there is no such employee:
500 Internal Server Error
If you are the administrator of this website, then please read this web application's log file and/or the web server's log file to find out what went wrong
How do I get errors to go to my new error pages?
Code:
config/environments/development.rb:
-------
# see error pages in dev site
config.consider_all_requests_local = false # true
config/application.rb:
-------
# added for error pages
config.exceptions_app = self.routes
config/routes.rb
------
get "/404", to: "errors#not_found", :via => :all
get "/500", to: "errors#internal_server_error", :via => :all
controllers/errors_controller.rb
-----
class ErrorsController < ApplicationController
def not_found
respond_to do |format|
format.html { render status: 404 }
end
end
def internal_server_error
respond_to do |format|
format.html { render status: 500 }
end
end
end
---> these work so I wont paste the content here.
app/views/errors/not_found.html.erb
app/views/errors/internal_server_error.html.erb
Is this the right approach? If Yes, what am I missing or not understanding.
Thanks for any help.
You need to add these in your top level (application) controller and rescue accordingly. Some sample code from a project I'm working on:
rescue_from ActiveRecord::RecordNotFound do |e|
puts "#{e.inspect}, PARAMS: #{params.inspect}" if Config[:verbose_tests]
respond_with_404
end
rescue_from ActionController::RoutingError do |e|
puts "#{e.inspect}, PARAMS: #{params.inspect}" if Config[:verbose_tests]
respond_with_404
end
rescue_from Pundit::NotAuthorizedError do
respond_with_403
end
In your case, this would be something like:
// app/controllers/application_controller.rb
rescue_from ActiveRecord::RecordNotFound do
not_found
end
rescue_from ActionController::RoutingError do
not_found
end
rescue_from ActionController::StandardError do
internal_server_error
end
Make sure you have access to your internal_server_error and not_found methods inside your application controller, you can even define them there, and then when the error is thrown your app should respond accordingly.

No route matches {:action=>"show" ... missing required keys: [:id]

Seeing many question related to this but none of them gives answer to my problem.
I have Rails api application without ActiveRecord support. It is easy to reproduce problem. Follow steps:
Create rails api site without ActiveRecord support
rails new test001 -O --api
Change folder to test001 and run:
rails g scaffold testapp id name
Create model file testapp.rb in app/models folder
class Testapp
include ActiveModel::Model
attr_accessor :id, :name, :created_at, :updated_at
def self.all
t1 = Testapp.new(:id =>111, name: "t111")
return [t1]
end
end
Start server
rails s
Using postman REST client create GET request
http://localhost:3000/testapps.json
It fails with error ActionView::Template::Error (No route matches {:action=>"show", :controller=>"testapps", :format=>:json, :id=>#<Testapp:0x00000005411518 #id=111, #name="t111">} missing required keys: [:id]):
I have dummy implementation for POST, PUT, GET 1 item and all works. Here is dummy implementation of GET 1 item (/testapps/x.json)
def self.find(p)
return Testapp.new(:id =>p, name: "t123")
end
What is the problem with GET all (/testapps.json)?
Found solution.
Problem is scaffold generated index.json.jbuilder
file:
json.array!(#testapps) do |testapp|
json.extract! testapp, :id, :id, :name
json.url testapp_url(testapp, format: :json) #REMOVE
end
It added line json.url testapp_url(testapp, format: :json) for no reason. json.extract! deserialized object already.
Removing line solved problem.
I still do not know why testapp_url(testapp, format:json) caused error. Checking Rails Routing document http://guides.rubyonrails.org/routing.html

UnknownFormat in rails spec

I'm fairly new to rails and I'm trying to return a javascript file from a controller action. In TDD fashion I'm trying to write a failing test first and although I've fixed a bunch of reasons it's failed since starting, I'm now up against an error that I'm not sure how to fix. If I run it in a browser, it works. But running the spec I get an error "ActionController::UnknownFormat" pointing to the respond_to do |format| line.
Here's the controller action...
def job_board
respond_to do |format|
format.js
end
end
And my spec
require 'rails_helper'
RSpec.describe ApiController, :type => :controller do
describe "when using an invalid url/api key" do
it "should return error" do
get :job_board
expect(response).to have_http_status(400) #bad request
end
end
end
And finally my routes.
Rails.application.routes.draw do
get 'api/job_board' => 'api#job_board', defaults: { format: 'js'}
end
Any idea why the spec is throwing this error? Thanks.
Got it figured out. I had to change the
get :job_board
in the spec to
get :job_board, format: :js

"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