UnknownFormat in rails spec - ruby-on-rails-4

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

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.

Cannot find existing route

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

How to write rspec test cases to cover failure of object save

I have following code
#support_report.notes = params[:support_report][:notes]
if #support_report.save
redirect_to admins_support_reports_path, notice: t('admins.support_reports.add_notes.successfully')
else
redirect_to admins_support_reports_path, alert: t('admins.support_reports.add_notes.no_change')
end
In this am just updating 'notes' attribute which is not having any validation.
I have to cover 'else' part inside rspec coverage.
How can I do it, please suggest some way
it "should re-render new template on failed save" do
SupportReport.any_instance.stubs(:valid?).returns(false)
post 'create'
assigns[:support_report].should be_new_record
response.should render_template('new')
end

undefined method `model_name' for #<User:XXXXXXXX>

I have a dashboard containing 2 partials. One partial works fine and does what its suppose to (bill). The other partial is setup similar to bill but wont work(request). When I look at my log it shows that the tenant(user) is being queried, also, the 1st partial is queried but the 2nd partial doesn't query. when I went to my dashboard controller and changed the instance of the partial to (Request.new) it works but I can't seem to get it to work right thru the controller. I don't want to have the method in the model. I am using mongoid.
SO here is my render in the dashboard...
<%= render partial: "request", locals: {request: #request} %>
In the partial I have...
<%= form_for [:tenants, request] do |f| %>
And on the dashboard controller I have...
def show
#dashboard = current_tenant
#bill = current_tenant.bill || current_tenant.build_bill
#request = current_tenant.request || current_tenant.build_request
end
(if I change #request = Request.new it works fine but I know that's not right)
The bill partial works and the dashboard finds the tenant but I keep getting
"undefined method `request' for #
Any idea of what I am missing? I compared the bill controller to the request controller and I cant find any differences. When I put the Model.new into the dashboard controller it works but I know this isn't right, its as if the app wont recognize the Request controller...
The error is saying it doesn't recognize "request" method.
Also here is my controller for request...
class Tenants::RequestsController < ApplicationController
before_filter :authenticate_tenant!
def index
#requests = Request.all
end
def show
#request = Request.find(params[:id])
end
def create
if #request = current_tenant.create_request(authorization_params)
redirect_to tenants_dashboard_path, :notice => "#{request.manager_name} has been Authorized!"
else
redirect_to tenants_dashboard_path, :error => "#{request.manager_name} has NOT been Authorized, please try again."
end
end
def edit
end
def update
if current_tenant.request.update_attributes(authorization_params)
redirect_to tenants_dashboard_path, :notice => "You have approved #{request.manager_name} to review your report"
else
redirect_to tenants_dashboard_path, :notice => "#{request.manager_name} is NOT allowed to review your report"
end
end
def destroy
#request = Request.find(params[:request_id])
name = #request.name
if #request.destroy
flash[:notice] = "\"#{name}\" was successfully removed from your profile."
redirect_to #dashboard
else
flash[:error] = "There was an error deleting this managers access."
render :show
end
end
Well it looks like
current_tenant.request has an issue. That means that the method is not available. Assuming you're not trying to access the http request , then you have an issue with the request method.
So your issue is with how you defined the request method (maybe in your model). e.g. is it a class method or a instance method etc.
Without knowing your goal, that's the general answer I can give you. Creating a Request.new could be right depending on your goal, but if your goal is to call the request method, you must make it available to current_tenant
One controller shouldn't be calling your other controller as you have suggested...

"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.