My test example is:
it "routes to #add_role" do
post("/users/1/add_role").should route_to("users#add_role",id: 1)
end
Here is the failure message:
UsersController routing routes to #add_role
Failure/Error: post("/users/1/add_role").should route_to("users#add_role",id: 1)
The recognized options <{"controller"=>"users", "action"=>"add_role", "id"=>"1"}> did not match <{"id"=>1, "controller"=>"users", "action"=>"add_role"}>, difference: <{"id"=>1}>.
<{"id"=>1, "controller"=>"users", "action"=>"add_role"}> expected but was
<{"controller"=>"users", "action"=>"add_role", "id"=>"1"}>.
My environment:
ruby-1.9.3-p194
Rails 3.2.6
Rspec (2.10.0)
You need id in the route_to to be string.
Rails does not type cast into numbers as it makes no assumptions about types
Related
I am trying to set a session variable in a request spec.
I have tried the following things to do this:
RSpec.describe 'Application Controller' do
context 'updating an application' do
before :each do
#app = create(:application, step: 'investigation')
end
it 'should update app status' do
Status.create(app_id: #app.id, name: Status.names[:start])
request.session[:app_id] = #app.id
patch "/applications/start",
params: s_params
expect(response).to redirect_to(offers_path)
end
end
end
I have tried substituting request with #request both result in the same output.
NoMethodError:
undefined method `session' for nil:NilClass
then I have tried just setting as:
session[:app_id] = #app.id
which will yield:
NoMethodError:
undefined method `session' for nil:NilClass
and also setting it like this:
patch "/applications/start",
params: s_params,
session: {"app_id" => #app.id}
which will yield:
ArgumentError:
unknown keyword: session
My versions:
╰>>> ruby -v
ruby 2.4.5p335 (2018-10-18 revision 65137) [x86_64-darwin18]
╰>>> rails -v
Rails 5.2.1
╰>>> rspec -v
RSpec 3.8
- rspec-core 3.8.0
- rspec-expectations 3.8.1
- rspec-mocks 3.8.0
- rspec-rails 3.8.0
- rspec-support 3.8.0
Looking at the documentation it suggests we could leverage the sessions but does not give a clear example of how we would do this.
First of all, little remark...
I recommend using 'type:' for RSpec test definition. It's good in anyways: easy to determinate by review and we will have the ability to define some extensions/customization for that type of tests(if needed).
The second point
We don't have request / response / controller methods(instance) before the request. Only after request, we will have them. By this reason we can't use request.session[:app_id] = #app.id before patch in your example...
The third point
#app it is a risky name of variable, not sure it will be a problem or not, but will be better to rename it
One of the possible solution
You can try to use some stubs for it, for example:
allow_any_instance_of(ActionDispatch::Request).to receive(:session) { { app_id: '11' } }
Notes
The requests methods get / post / patch / and etc are differrent from similar controller test methods. All they are handling by process method. More details you can find here
The most straightforward approach that comes into my mind is to mock the controller before doing the request:
session = { app_id: #app.id }
allow_any_instance_of(SomeController).to receive(:session).and_return(session)
get some_resources_path
expect(response).to ...
I have a /bar/foos path that routes to FoosController. GETting /bar/foos routes to FoosController#index, which works as expected when testing manually.
I inherited some routing tests for this which worked fine with Rails 4.2, but started breaking when updating to Rails 5.0.
For some reason RSpec is creating a id: :foos parameter in expect(get: '/bar/foos') in this spec:
require 'spec_helper'
describe FoosController do
describe 'routing' do
it 'get /bar/foos' do
expect(get: '/bar/foos').to route_to('foos#index')
end
end
end
Which yields this error:
The recognized options <{"controller"=>"foos", "action"=>"index", "id"=>"foos"}>
did not match <{"controller"=>"foos", "action"=>"index"}>, difference:.
--- expected
+++ actual
## -1 +1 ##
-{"controller"=>"foos", "action"=>"index"}
+{"controller"=>"foos", "action"=>"index", "id"=>"foos"}
I am using Rails 5.0.2 and rspec 3.6.0.beta2.
I generated a scaffold with Rails (4.1.16) and Rspec (3.5.1).
It generated this test:
describe "GET #show" do
it "assigns the requested team as #team" do
team = Team.create! valid_attributes
get :show, params: {id: team.to_param}, session: valid_session
expect(assigns(:team)).to eq(team)
end
end
Which outputs this error:
TeamsController GET #show assigns the requested team as #team
Failure/Error: get :show, params: {id: team.to_param}, session: valid_session
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"teams", :params=>{:id=>"82"}, :session=>{}}
If I remove the keys to the parameters to get, i.e.:
get :show, {id: team.to_param}, valid_session
The test passes fine.
Not sure what gem defines the generator template (rspec-rails?) and why I get this error. Help would be appreciated understanding this issue. Thanks.
The generator (rspec:scaffold, which comes with rspec-rails) is generating tests with the syntax required by Rails 5 (see the last section of that blog post), which is not compatible with Rails 4. I think this is a bug in rspec-rails, since rspec-rails 3.5 is otherwise compatible with Rails 4. (I'm using those versions together myself; I just haven't used the generator.)
rspec-rails was changed to use the Rails 5 syntax in rspec-rails 3.5.0.beta4, so one workaround is to use rspec and rspec-rails 3.4 — not so nice since the newer versions have features and fixes which are as useful with Rails 4 as with Rails 5. Another workaround is to manually fix the output of the generator as you did.
in my Gemfile:
group :test do
gem 'minitest-spec-rails'
end
my test file in rail_root/test/functional/publisher_controller_test.rb
# -*- encoding : utf-8 -*-
describe PublisherController do
describe "GET #signin" do
it "responds successfully with an HTTP 200 status code" do
get :signin
assert_response :success
end
end
end
my unit test worked fine, but when I run
ruby -Itest test\functional\publisher_controller_test.rb"
it went wrong , here is the error:
test/functional/publisher_controller_test.rb:2:in `<main>':
uninitialized constant PublisherController (NameError).
I just don't know why it can find my model but can not find the controller.
Rails expects controller names to be plural, like so:
PublishersController
Your test is using a singular controller name, PublisherController, which doesn't exist. This can be caused by a typo when using generators.
To fix it, change Publisher to Publishers. Remember to change the filename as well.
After digging fairly deeply on this issue, I've come to an impasse between my understanding of the documentation and my results.
According to https://www.relishapp.com/rspec/rspec-rails/v/2-8/docs/routing-specs/route-to-matcher, we should be able to write the following:
#rspec-rails (2.8.1)
#rspec (>= 1.3.1)
#rspec-core (~> 2.8.0)
# routing spec
require "spec_helper"
describe BusinessUsersController do
describe "routing" do
it "routes to some external url" do
get("/business_users/7/external_url").should route_to("http://www.google.com")
end
end
end
# routes.rb
BizeebeeBilling::Application.routes.draw do
resources :business_users do
member do
get "external_url" => redirect("http://www.google.com")
end
end
end
Running this spec produces the following results:
Failures:
1) BusinessUsersController routing routes to some external url
Failure/Error: assert_routing "/business_users/7/external_url", "http://www.google.com"
ActionController::RoutingError:
No route matches "/business_users/7/external_url"
# ./spec/routing/business_users_routing_spec.rb:19:in `block (3 levels) in <top (required)>'
I have not been able to find anyone reporting this specific issue anywhere.
Added detail: the route is resolved perfectly well when testing manually.
Routing specs/tests specialize in testing whether a route maps to a specific controller and action (and maybe some parameters too).
I dug into the internals of Rails and Journey a bit. RSpec and Rails (basically, some details left out) use Rails.application.routes.recognize_path to answer the question "is this routable?"
For example:
$ rails console
> Rails.application.routes.recognize_path("/business_users/1", method: "GET")
=> {:action=>"show", :controller=>"business_users", :id=>"1"}
However, there's no controller on the other end of /business_users/1/external_url. In fact, to perform the redirect, Rails has created an instance of ActionDispatch::Routing::Redirect, which is a small Rack application. No Rails controller is ever touched. You're basically mounting another Rack application to perform the redirection.
To test the redirect, I recommend using a request spec instead (a file in spec/requests). Something like:
require "spec_helper"
describe "external redirection" do
it "redirects to google.com" do
get "/business_users/1/external_url"
response.should redirect_to("http://www.google.com")
end
end
This tests the route implicitly, and allows you to test against the redirection.
Andy Lindeman has the correct answer. However, you don't have to put the spec in spec/requests, you can keep it in spec/routing and be explicit with the metadata "type": describe 'my route', type: :request do
I was running into a similar case where I was trying to test a series of routes, some which should redirect and some which shouldn't. I wanted to keep them in a single routing spec, since that was the most logical way to group them.
I tried using describe: 'my route', type: request, but found that not to work. However, you can include RSpec::Rails::RequestExampleGroup in your spec context to gain access to the request spec methods. Something like:
describe "My Routes" do
context "Don't Redirect" do
it "gets URL that doesn't redirect" do
get("business_users/internal_url").should route_to(controller: "business_users", action: "internal_url_action")
end
end
context "Redirection" do
include RSpec::Rails::RequestExampleGroup
it "redirects to google.com" do
get "/business_users/1/external_url"
response.should redirect_to("http://www.google.com")
end
end
end
The simplest way to test external redirects is to use an integration test:
test "GET /my_page redirects Google" do
get "/my_page"
assert_redirected_to "https://google.com"
end
You test needs to be under your test/integration directory or the equivalent directory where the integration tests should go.
I think you want the redirect_to matcher.