Rails assert_routing on root route raises ActionController::RoutingError - ruby-on-rails-3

I'm trying to test that requesting the root path (/) routes to my beta controller's :new action. When I do this manually (in my browser), it works fine. But my automated test is failing with No route matches "/".
In config/routes.rb I have
MyRailsApp::Application.routes.draw do
root to: 'beta#new' # Previously: `redirect('/beta/join')`
end
In my spec/routing/root_route_spec.rb I have tried
require 'spec_helper'
describe "the root route" do
it "should route to beta signups" do
get('/').should route_to(controller: :beta, action: :new)
end
end
and have also tried
require 'spec_helper'
describe "the root route" do
it "should route to beta signups" do
assert_routing({ method: :get, path: '/' }, { controller: :beta, action: :new })
end
end
but both complain that No route matches "/"
1) the root route should route to the beta signups
Failure/Error: get('/').should route_to "beta#new"
No route matches "/"
# ./spec/routing/root_route_spec.rb:5:in `block (2 levels) in <top (required)>'
When I go, in my browser, to localhost:3000, I'm correctly routed to the BetaController::new action.
What explains the No route matches "/" error?
I'm using Rails 3.1.3 and RSpec-2.10.
Thanks!

You should test that / redirects to /beta/join and then as a separate issue test that /beta/join routes to the :new action of the :beta controller.
Redirection is tested in requests, not routing.
# spec/requests/foobar_spec.rb
describe 'root' do
it "redirects to /beta/join" do
get "/"
response.should redirect_to("/beta/join");
end
end
and
# spec/routing/beta_spec.rb
...
it 'routes /beta/join to the new action'
get('beta/join').should route_to(:controller => 'beta', :action => 'new')
end

Related

Rails API post in request spec should pass params but getting empty

To start, in my request spec sites.spec.rb I have this test:
describe "POST /v1/sites" do
let(:valid_attributes) { { url: "www.example.com", site_code: "123456" } }
context 'when the request is valid' do
before { post v1_sites_path, params: valid_attributes }
it 'creates a site' do
expect(json['url']).to eq("www.example.com")
expect(json['site_code']).to eq("123456")
end
it 'returns status code 201' do
expect(response).to have_http_status(201)
end
end
I then get a failing test for "creates a site"...
1) Sites POST /v1/sites when the request is valid creates a site
Failure/Error: expect(json['url']).to eq("www.example.com")
expected: "www.example.com"
got: ["can't be blank"]
(compared using ==)
# ./spec/requests/sites_spec.rb:61:in `block (4 levels) in <top (required)>'
# ./spec/rails_helper.rb:84:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:83:in `block (2 levels) in <top (required)>'
Now this technically makes sense because my Site model has validates :url, :site_code, presence: true. So the test is failing because the post is not passing the params correctly.
Lastly, here is the controller:
module Api::V1
class SitesController < BaseApiController
before_action :set_site, only: [:show, :update, :destroy]
# GET /sites
def index
#sites = Site.all
render json: #sites
end
# GET /sites/1
def show
render json: #site
end
# POST /sites
def create
#site = Site.new(site_params)
if #site.save
render json: #site, status: :created, location: #site
else
render json: #site.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /sites/1
def update
if #site.update(site_params)
render json: #site
else
render json: #site.errors, status: :unprocessable_entity
end
end
# DELETE /sites/1
def destroy
#site.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_site
#site = Site.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def site_params
# params.require(:data).require(:attributes).permit(:url, :side_code, :user_id)
# params.require(:site).permit(:url, :side_code, :user_id)
params.fetch(:site, {}).permit(:url, :side_code)
end
end
end
I am speculating that the way I am passing parameters to the post for Rails API is perhaps not formatted or correctly or something else entirely. I did play with the params in the test block trying data: { attributes: valid_attributes } with no luck.
Any thoughts or suggestions are greatly appreciated!
This problem was indeed due to format of the parameters I was passing to POST request in the test block. I tested the POST via the command line and watched the rails server to see how the params were coming through. They looked like this:
Parameters: {"site_code"=>"123456", "url"=>"www.updated.com", "subdomain"=>"api", "id"=>"2", "site"=>{"site_code"=>"123456", "url"=>"www.updated.com"}}
Then in my sites_spec.rb, I copied this format for the valid params of the post request:
let(:valid_attributes) { { "site"=>{"url"=>"www.example.com", "user_id"=>user_id, "site_code"=>"123456"} } }
This works. The JSON format of the params needed to be formatted in the test block the same way they would be if it was real JSON request.

No route matches [GET] "/products"

i follow this How To but i have No route matches error...
i have this routes.rb:
# config/routes.rb
require 'api_constraints'
Rails.application.routes.draw do
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :products, only: [:index, :show, :create, :destroy]
end
end
end
Rake Routes:
api_products GET /products(.:format) api/v1/products#index {:format=>:json, :subdomain=>"api"}
POST /products(.:format) api/v1/products#create {:format=>:json, :subdomain=>"api"}
api_product GET /products/:id(.:format) api/v1/products#show {:format=>:json, :subdomain=>"api"}
DELETE /products/:id(.:format) api/v1/products#destroy {:format=>:json, :subdomain=>"api"}
This is the error:
ActionController::RoutingError (No route matches [GET] "/products")
I presume that the request is getting rejected at subdomain constraint. I think you make request to http://localhost:3000/produtcts, but you need some url like http://api.****/products. There is a guide how to do it locally http://shapeshed.com/developing-subdomain-rails-sites-locally/

devise and rspec-rails - How to sign-in user in Request type specs (specs tagged with type: :request)?

Environment
Rails 4.2.0
ruby-2.2.1 [ x86_64 ]
devise 3.4.1
rspec-core 3.2.2
rspec-rails 3.2.1
In my /spec/rails_helper.rb I have included Devise helpers for spec files tagged with type: :controller and type: :request
spec/rails_helper.rb
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
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:suite) do
begin
DatabaseCleaner.start
FactoryGirl.lint
ensure
DatabaseCleaner.clean
end
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run # ==================> L-60
end
end
config.include FactoryGirl::Syntax::Methods
# 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
config.include Devise::TestHelpers, type: :request
end
With that config in place the type: controller specs runs fine. However when running type: request specs I am getting following error:
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `env' for nil:NilClass
# /home/.rvm/gems/ruby-2.2.1#myapp/gems/devise-3.4.1/lib/devise/test_helpers.rb:24:in `setup_controller_for_warden'
# ./spec/rails_helper.rb:60:in `block (3 levels) in <top (required)>'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/generic/base.rb:15:in `cleaning'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/base.rb:92:in `cleaning'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:86:in `block (2 levels) in cleaning'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:87:in `call'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:87:in `cleaning'
# ./spec/rails_helper.rb:59:in `block (2 levels) in <top (required)>'
https://github.com/plataformatec/devise/blob/master/lib/devise/test_helpers.rb#L24 is following
def setup_controller_for_warden #:nodoc:
#request.env['action_controller.instance'] = #controller # ==================> L-24
end
I am aware that #request instance is not available for :request type specs and hence the error.
Are there any helpers available we can use to sign-in a user in :request type specs when using Devise?
I found a similar issue https://github.com/plataformatec/devise/issues/1114, the reply to which suggests following:
If you're doing integration tests, make sure to sign in your user in the tradicional way, by filling the sign in form and submitting.
But I would like to by pass the actual login for specs which requires a signed-in user.
Thanks.
With the help of a few SO posts(please refer to the References section below) I have managed to achieve the desired solution. I am posting my working code below, in case it can help others looking out for the same:
spec/rails_helper.rb
RSpec.configure do |config|
....
....
config.include Devise::TestHelpers, type: :controller
config.include Warden::Test::Helpers, type: :request
end
spec/shared_contexts.rb
RSpec.shared_context "api request global before and after hooks" do
before(:each) do
Warden.test_mode!
end
after(:each) do
Warden.test_reset!
end
end
RSpec.shared_context "api request authentication helper methods" do
def sign_in(user)
login_as(user, scope: :user)
end
def sign_out
logout(:user)
end
end
/spec/requests/api/logout_spec.rb
require 'rails_helper'
require 'shared_contexts'
RSpec.describe "Api Logout", :type => :request do
include_context "api request authentication helper methods"
include_context "api request global before and after hooks"
let(:email) { 'test_user1#example.com' }
let(:password) { 'password' }
# Assumes you have FactoryGirl included in your application's test group.
let!(:user) { create(:user, email: email, password: password) }
context "DELETE /logout" do
it "responds with 204 and signs out the signed-in user" do
sign_in(user)
# Till not figured out how to assert Warden has successfully logged in the user like we can do in a Devise controller spec by asserting subject.current_user. If anybody knows a way to do it please share.
# expect(subject.current_user).to_not be_nil
delete "/logout"
expect(response).to have_http_status(204)
end
end
end
I have still not figured out how to assert Warden has successfully logged in the user like we can do in a Devise controller spec by asserting expect(subject.current_user).to_not be_nil. If anybody knows a way to do it please share.
References
Integration test with rspec and devise sign_in env
https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
https://github.com/hassox/warden/blob/master/lib/warden/test/helpers.rb
undefined method 'env' for nil:NilClass
https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs
The code in the above link still relies on a request object which is only available in Controller specs. Thus not useful for type: :request specs.
https://github.com/plataformatec/devise/issues/3555
Thanks,
Jiggnesh
While the popular answer here, also replicated on the Devise wiki, is ok, it is simplest to just:
spec/rails_helper.rb
RSpec.configure do |config|
# ...
config.include Devise::Test::IntegrationHelpers, type: :request
end
And just use sign_in in your request spec. This is the equivalent of declaring include Devise::Test::IntegrationHelpers in an system/feature spec or Rails system/controller test.
Reference
Devise wiki: How to sign in and out a user in request type specs - Simple approach

Why is Rails 3.2.2 Generating URLs prefixed with /assets when using redirect_to?

Well the title Question pretty much sums it up, But I'd like to detail a scenario anyways,
I've created a DemoController, (I have not created a Resource model), and my routes.rb looks like this:
DispatchMe::Application.routes.draw do
root to: "demo#index"
end
From the demo controller I'm dong the following:
class DemoController < ApplicationController
def index
redirect_to :action => 'show'
end
def show
end
end
There is a file in: app/views/demo/show.html.erb of course, And I'd expected that template to be rendered but instead I'm getting the following error:
ActionController::RoutingError (No route matches [GET] "/assets")
and this URL is generated as a result from the redirect:
/assets?action=show&controller=demo
Am I missing something here? I thought rails was supposed to render the action's template for such cases.
Note. I understand that If I create a route like get 'show' => "demo#show" and call redirect_to show_path it'll work just fine, But I need to know if that's mandatory?
Thank you very much!
For the desired behavior, use render instead of redirect_to:
class PagesController < ApplicationController
def index
render :action => "show"
end
def show
end
end
EDIT:
You can use redirect_to on other actions, but from what I can tell, the index action sets the base path. To simplify route definition, use resources :controller_name. You can view the routes generated by resources by typing rake routes in your command line.
Example:
demo_controller.rb
class DemoController < ApplicationController
def index
end
def show
redirect_to :action => 'index'
end
end
routes.rb
DispatchMe::Application.routes.draw do
root to: "demo#index"
resources :demo
end
development.log
Started GET "/demo/show" for 127.0.0.1 at 2012-04-04 14:55:25 -0400
Processing by DemoController#show as HTML
Parameters: {"id"=>"show"}
Redirected to http://dispatch.dev/
Completed 302 Found in 0ms (ActiveRecord: 0.0ms)

Rails 3 I18n routes

I have such routes in my app:
# config/routes.rb
Demo::Application.routes.draw do
root :to => "requests#index"
match 'find' => 'requests#find'
get "about/developer"
get "about/api"
end
All works ok.
But I want to enable I18n urls and changed routes: (by the official Rails guide):
# config/routes.rb
Demo::Application.routes.draw do
scope "(:locale)" do
root :to => "requests#index"
get "about/developer"
get "about/api"
match 'find' => 'requests#find'
end
end
After adding scope lines it gives error:
Exiting
C:/RailsInstaller/Ruby1.8.7/lib/ruby/gems/1.8/gems/actionpack-3.0.3/lib/action_dispatch/routing/mapper.rb:160:in
`default_controller_and_action':
missing :controller (ArgumentError)
What's up? Official guide is wrong?
My Rails version: 3.0.3, Ruby 1.8.7
Does it work if you specify all of the controller/action names?
In other words, try changing:
get "about/developer"
get "about/api"
to:
get "about/developer" => "about#developer"
get "about/api" => "about#api"