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