Unable to test controller action in rspec - ruby-on-rails

I am trying to test a controller action on a non-restful route.
config/routes.rb:
match '/integration/:provider/callback' => "providers#manual_auth", as: :integration_callback
You can see that also via rake routes:
integration_callback /integration/:provider/callback(.:format) providers#manual_auth
In my spec file:
spec/controllers/providers_controller_spec.rb:
describe ProvidersController do
describe '#manual_auth' do
it 'hits the manual_auth action' do
get :manual_auth, use_route: :integration_callback
end
end
end
That gives me an error of:
Failures:
1) ProvidersController#manual_auth hits the manual_auth action
Failure/Error: get :manual_auth, use_route: :integration_callback
ActionController::RoutingError:
No route matches {:controller=>"providers", :action=>"manual_auth"}
However in app/controllers/providers_controller.rb I have
class ProvidersController < ApplicationController
def manual_auth
logger.info "Got into manual auth"
end
end
I should mention I'm purposely avoiding a request spec here because I need to be able to access and set a session object(that lives in this #manual_auth action) which apparently can only be done in controller tests, not request specs.

The integration_callback has one parameter, which is :provider.
Try this:
get :manual_auth, provider: 'test', use_route: :integration_callback

Related

how to make a custom json rails routes and make the tests pass

Edit 2: OMG I AM SO STUPID. In my spec I have a let(:response) {MyModel.create()} so thats why its failing. Going to delete post
(edited for clarity)
In my routes file
root "search_email#index"
get "search_email/retrieve_last_user_survey" => "search_email#retrieve_last_user_survey"
Controller
class SearchEmailController < ApplicationController
def retrieve_last_user_survey
render :json => "")
end
end
Spec file
require "rails_helper"
RSpec.describe SearchEmailController, type: :controller do
describe 'GET #retrieve_last_user_survey' do
before do
get :retrieve_last_user_survey, :params => { :email => 'abc#abc.com'}
end
it "returns http success" do
expect(response).to have_http_status(:success)
end
end
end
When try to run my test, i get this
Failure/Error: expect(response).to have_http_status(:success)
expected a response object, but an instance of Relational::Response (custom model name) was received
I have no idea why I am not getting a response object, I know I am hitting the controller method cause I inserted puts and I can see it.
Also on a semi related note. If i create a button that hits this route. why does it redirect me to a show route. I thought it would just return some http request that i can see in the dev console. I know cause said I dont have a show route or a show template.
It's not meant to be facetious, but to get the test to pass, replace the render line in the controller with:
head :ok
Does the test pass? Probably. So now add some expectation on the content header, and then finally the content itself.
If you break it down into small pieces, you should find the problem. It's not obvious from what you've shared, we can't see into the controller method.

RSpec No route matches {:action=>"/dashboard/sites/24/attendance_summer_city_camp_integrativ.xlsx", :controller=>"dashboard/sites"}

I have a controller /dashboard/sites_controller.rb, within which I have an action attendance_summer_city_camp_integrativ
def attendance_summer_city_camp_integrativ
#site = Site.find(params[:site_id])
respond_to do |format|
format.xlsx
end
end
It returns an Excel, as you can see.
config/routes.rb
namespace :dashboard do
resources :sites do
get :attendance_summer_city_camp_integrativ
end
end
In my test file, when I do a get request on this address I get
No route matches {:action=>"/dashboard/sites/24/attendance_summer_city_camp_integrativ.xlsx", :controller=>"dashboard/sites"}
describe 'GET #attendance_summer_city_camp_integrativ' do
subject do
get dashboard_site_attendance_summer_city_camp_integrativ_path(site.id, format: :xlsx)
end
end
Failure/Error: get dashboard_site_attendance_summer_city_camp_integrativ_path(site.id, format: :xlsx)
ActionController::UrlGenerationError:
No route matches {:action=>"/dashboard/sites/24/attendance_summer_city_camp_integrativ.xlsx", :controller=>"dashboard/sites"}
When I go to this address manually in the browser it works fine.
Started GET "/dashboard/sites/6/attendance_summer_city_camp_integrativ.xlsx" for 127.0.0.1 at 2019-06-28 18:18:46 +0200
Processing by Dashboard::SitesController#attendance_summer_city_camp_integrativ as XLSX
What am I doing wrong?
Perhaps this:
describe 'GET #attendance_summer_city_camp_integrativ' do
subject do
get dashboard_site_attendance_summer_city_camp_integrativ_path(site.id, format: :xlsx)
end
end
Should be more like this:
describe 'GET #attendance_summer_city_camp_integrativ' do
subject do
get attendance_summer_city_camp_integrativ, {site_id: site.id, format: :xlsx}
end
end
(I guess, according to the docs, you may need to form your params differently for rails 5 v. pre-rails 5.)
You have an action called attendance_summer_city_camp_integrativ, but your test is trying to call the action /dashboard/sites/24/attendance_summer_city_camp_integrativ.xlsx. Which isn't an action, but a fully-formed url. So, maybe calling get dashboard_site_attendance_summer_city_camp_integrativ_path instead of get attendance_summer_city_camp_integrativ is the issue?

No route matches in functional/controller test

I have the following controller test using Minitest::Rails and Rails 4. When I run it, I get an error: ActionController::UrlGenerationError: No route matches {:action=>"/", :controller=>"foo"} error, despite it being defined.
The whole point of this is to test methods that are on ApplicationController (FooController exists only in this test and is not a placeholder for the question).
class FooController < ApplicationController
def index
render nothing: true
end
end
class FooControllerTest < ActionController::TestCase
it 'does something' do
with_routing do |set|
set.draw do
root to: 'foo#index', via: :get
end
root_path.must_equal '/' #=> 👍
get(root_path).must_be true #=> No route matches error
end
end
end
There a number of similar questions on StackOverflow and elsewhere, but they all refer to the issue of route segments being left out (e.g. no ID specified on a PUT). This is a simple GET with no params, however.
I get the same result if the route is assembled differently, so I don't think it's the root_path bit doing it (e.g. controller :foo { get 'test/index' => :index }).
I did some search on what information you have provided. I found an issue open in rspec-rails gem. However gem doesn't matter here but fundamentally they said its context problem. When you call with_routing it doesn't executed in correct context, so gives error of No Route matches.
To resolve issue, I tried locally with different solution. Here is what I have tried
require 'rails_helper'
RSpec.describe FooController, type: :controller do
it 'does something' do
Rails.application.routes.draw do
root to: 'foo#index', via: :get
end
expect(get: root_path).to route_to("foo#index")
end
end
In above code, the major problem is it overwrite existing routes. But we can reproduce routes with Rails.application.reload_routes! method.
I hope this helps to you!!
UPDTATE
I tried to understand your last comment and dig into get method. When we call get method it takes argument of action of controller for which we are doing test. In our case when we do get(root_path) it tries to find foo#/ which is not exists and hence gives no route matches error.
as our main goal is to check root_path routes are generated correctly, we need to use method assert_routing to check it. Here is how I test and it works
assert_routing root_path , controller: "foo", action: "index"
Full code :
require 'test_helper'
class FooControllerTest < ActionController::TestCase
it 'does something' do
with_routing do |set|
set.draw do
root to: 'foo#index', via: :get
end
root_path.must_equal '/' #=> true
assert_routing root_path , controller: "foo", action: "index" #=> true
get :index
response.body.must_equal ""
end
end
end
I read things from official document : http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html
if you check the source of ActionController::TestCase#get method, it expects action name, e.g. :index, :create, 'edit, 'create'
if you pass root_path on #get method, absolutely it will raise error, because root_path method returns '/'.
I just checked to add :/ method to FooController
class FooController
def index
end
def /
end
end
Rails.application.routes.draw do
root 'foo#index'
get 'foobar' => 'foo#/'
end
when I was visiting http://localhost:3000/foobar, rails gave me
AbstractController::ActionNotFound (The action '/' could not be found for FooController): respond
I think '/' is not permitted action on rails, I don't do research further, because I think it's very reasonable.
You may write
assert_routing '/', controller: "foo", action: "index"
for current test, then you can write integration test to check root_path and other features.
Following are the source code of some methods I've talked above: (I'm using rails version 4.2.3 to test this interesting issue)
action_controller/test_case.rb
# Simulate a GET request with the given parameters.
#
# - +action+: The controller action to call.
# - +parameters+: The HTTP parameters that you want to pass. This may
# be +nil+, a hash, or a string that is appropriately encoded
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
# - +session+: A hash of parameters to store in the session. This may be +nil+.
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
#
# You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
# +post+, +patch+, +put+, +delete+, and +head+.
#
# Note that the request method is not verified. The different methods are
# available to make the tests more expressive.
def get(action, *args)
process(action, "GET", *args)
end
# Simulate a HTTP request to +action+ by specifying request method,
# parameters and set/volley the response.
#
# - +action+: The controller action to call.
# - +http_method+: Request method used to send the http request. Possible values
# are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+.
# - +parameters+: The HTTP parameters. This may be +nil+, a hash, or a
# string that is appropriately encoded (+application/x-www-form-urlencoded+
# or +multipart/form-data+).
# - +session+: A hash of parameters to store in the session. This may be +nil+.
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
#
# Example calling +create+ action and sending two params:
#
# process :create, 'POST', user: { name: 'Gaurish Sharma', email: 'user#example.com' }
#
# Example sending parameters, +nil+ session and setting a flash message:
#
# process :view, 'GET', { id: 7 }, nil, { notice: 'This is flash message' }
#
# To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
# prefer using #get, #post, #patch, #put, #delete and #head methods
# respectively which will make tests more expressive.
#
# Note that the request method is not verified.
def process(action, http_method = 'GET', *args)
# .....
end
The whole point of this is to test behavior inherited by the ApplicationController.
There is no behavior in your question related to ApplicationController other than the fact that FooController inherits from ApplicationController. And since there's no other behavior to test here in FooController related to something from the Application Controller ...
You can test this
class FooController < ApplicationController
def index
render nothing: true
end
end
with this
describe FooController, type: :controller do
describe '#index' do
it 'does not render a template' do
get :index
expect(response).to render_template(nil)
end
end
end
The solution to this problem was much simpler than expected:
# change this:
get(root_path)
# to this
get(:index)
The with_routing method works fine to define the path in this context.

Custom Rails route test fails despite the route loading OK in the browser

I am getting an error testing a controller route despite being able to successfully load it via the browser. Rails4 + rspec.
Any ideas?
#controller spec
describe PublicSitesController do
describe "GET index" do
it "returns success" do
get :index #line 7 in the spec file
response.status.should == 200
end
end
end
#routes
get ":site_name/:page_name", to: "public_sites#show"
get ":site_name", to: 'public_sites#index'
get "/", to: 'public_sites#root'
#controller
class PublicSitesController < ApplicationController
def root
end
def index
end
def show
end
end
#the error:
Failures:
1) PublicSitesController GET index returns success
Failure/Error: get :index
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"public_sites"}
# ./spec/controllers/public_sites_controller_spec.rb:7:in `block (3 levels) in <top (required)>'
Here are the relevant routes via rake routes:
GET /:site_name/:page_name(.:format) public_sites#show
POST /:site_name/:page_name(.:format) public_sites#receive_form
GET /:site_name(.:format) public_sites#index
GET / public_sites#root
You are missing some parameter on the request, the router doesn't know what to do with ":site_name", try something like:
get :index, site_name: 'something'
edit:
when you call get/post/etc inside a test you call the action name with that method, not the url, that way the controller test is independant of the url that make that action work (you can change the url and the controller will still work)
your route tells rails that it need some parameter named "site_name" so you need to tell rails what's inside "site_name" with a parameter for the action
if you want you can have routing tests, there you can test that some url goes to some controller's action with some value on some parameter https://www.relishapp.com/rspec/rspec-rails/v/2-3/docs/routing-specs
when you open the site on a browser you are not calling the action, you are actually running your whole application, then the routing system calls the controller's action
edit 2:
if you want to test the show action you should call it with
get :show, site_name: 'some_site', page_name: 'some_page'

Using rails' "post" in controller tests in rspec with scoping and protocol on routes

I have a rails project that is running out of a subdirectory of the base url in production and I want it to act that way in dev so as to make dev and prod as close as possible. I have the routes file set up like so:
Foo::Application.routes.draw do
def draw_routes
root :to=>'foo#home'
resources :cats, :only=>[:create] do
end
if Rails.env.production?
scope :protocol=>'http://' do
draw_routes
end
else
scope :path=>"/foo", :protocol=>'http://' do
draw_routes
end
end
end
My CatsController is like this:
class CatsController < ApplicationController
def create
#cat = Cat.new(:name=>"Bob")
if #cat.save()
redirect_to root
end
end
end
I want to test my Create Cat Method, so I set up an rspec test in spec/controllers/cats_controller_spec.rb:
require 'spec_helper'
describe CatsController do
describe "calling create cat with good data" do
it "should add a cat" do
expect {post(:action=>:create)}.to change(Cat, :count).by(1)
end
end
end
When I run my test, though, I get
Failure/Error: post :create
ActionController::RoutingError:
No route matches {:controller=>"cats", :action=>"create"}
# ./spec/controllers/cats_controller_spec.rb:5:in `(root)'
Rake routes says my route is there! What is going on under the hood here, and why is this failing?
Rake Routes:
cats POST /foo/cats(.:format) cats#create {:protocol=>"http://"}
The problem is that the value of :protocol is a bit wonky.
The better way to fix this, I think, is to set the protocol in your scopes to http instead of http://. If you did need to test your controller with some funky protocol, though, you should be able to do:
post(:action => :create, :protocol => 'funky')
or whatever your protocol might be.
For rspec 3, this below works for me.
post :action, protocol: 'https://'

Resources