Testing Rails Controller: get and post strange behavior - ruby-on-rails

I'm using RSpec (through the gem rspec-rails) for testing and developing
my application.
I've tried to "test" a controller and run up against a strange behavior of post and get methods (same for all the others of this kind).
In my route file:
controller :sessions do
post '/login', action: :login_create
get '/login', action: :login
get '/logout', action: :logout
end
At the beginning, I was thinking that post will simulate an http post
request at the specified url, so I've wrote in my spec:
describe "POST 'login'" do
it "returns http success" do
post 'login'
response.should be_success
response.should render_template 'sessions/login_create'
end
end
But this will call the login action, not the login_create, and then the last assert fail.
After a lot of googling and experiments, I've changed post 'login' with post :login_create and this actually works!
The strange thing is that also if I change post with get, it will continue to work! O_o
Isn't this strange? How this methods are intended to work and to be used?
In the Rails API I've not found anything else than the class: ActionController::TestCase::Behavior

What you're writing is a controller spec, not a request spec. In a controller spec, routes are not consulted at all because rspec invokes controller actions directly.
When you write post 'login', the login specifies the action name, not the URL path.
The correct way to test the login_create action would be to use post :login_create as you've discovered.

Related

RailsTutorial: what's the difference between signup_path and user_path?

I'm in chapter 7 of Michael Hartl's Rails Tutorial and I'm having a hard time understanding the "_path"s that he calls on for a particular test (section 7.3.4 to be exact). Here's the code in question:
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" }
end
assert_template 'users/new'
end
end
In particular, I'm confused about get signup_path and post users_path. When I go back and search the book for signup_path, Table 5.1 indicates it is the named route for [sample_app url]/signup which should direct visitors to the signup page of the website. Additionally, when I search the book for users_path, Table 7.1 indicates that it is the named route for [sample_app url]/users using the 'create' controller action because we are issuing a POST HTTP request.
NOTE: In responses, please don't suggest altering this code. This is based on Hartl's Rails Tutorial and the assumption is that the code is correct and should work as is. Please make sure your answers explain the code I've posted (or other code from his tutorial) and not modifications to the existing code.
EDIT: OK, so I think I can see the difference between signup_path and users_path: in the routes.rb file, /signup routes to users#new, which means it is routed to the users controller and specifically the 'new' method of that controller. I know the result of this is loading the new.html.erb file. Part of what I don't get is -- how does the users#new controller action know which HTML page to load? Does the 'new' method automatically call whichever view is also called 'new'? OR... (Here's the relevant controller code for the new method:
def new
#user = User.new
end
The other part of my problem is with users_path: rake routes says the POST method for /users is routes through the users#create controller/action. But my routes.rb file doesn't have a route to reflect this:
Rails.application.routes.draw do
root 'static_pages#home'
get '/help' => 'static_pages#help'
get '/about' => 'static_pages#about'
get '/contact' => 'static_pages#contact'
get '/signup' => 'users#new'
resources :users
end
So the second part of the question is: How does the routes file know to route the post users_path to the users#create controller/action?
The dont point to the same page, GET signup_path will GET you to the page where user can sign up while POST users_path is a different route where you send parameters that you have generate on GET signup_path.
Also you dont need to search tables to see to what URL each _path points, just type
rake routes
in command prompt and you will see what each path is.
This small line
resources :users
is actually a shortcut for seven different (but very common) routes that cover all CRUD (create, read, update, and delete) operations on a resource. One of them is exactly:
users_path POST /users users#create create a new user

functional test to confirm custom route with devise

I have a custom route for devise:
devise_scope :user do
get '/login' => "devise/sessions#new", :as => :new_user_session
get '/logout' => 'devise/sessions#destroy',
...
I want to make sure that when the request /login is called, that it gets correctly routed to devise and that the response is successful.
How do you test for custom routing and a successful request?
If you're using the built-in TestUnit to test, check out the Rails Guide on testing controllers.
If you're using Rspec, check out the Rspec Github Page for information on testing controllers.
It depends on you testing suite, personally I use RSpec + Capybara for my Rails projects.
If you don't have a testing suite yet I highly recommend this guide.
http://everydayrails.com/2012/03/12/testing-series-intro.html
In this particular instance, I'd say you want two tests. One for the routing and one for a successful request. Using rspec, this will look something like:
#spec/routing/devise_routing_spec.rb
require 'spec_helper'
describe "Devise Routes" do
it "should route the login correctly" do
{:get => "login"}.should route_to(controller: "devise/sessions", action: "new")
end
end
and
#spec/controllers/devise/session_controller_spec.rb
require 'spec_helper'
describe Devise::Sessions do
it "should be successful with a login request" do
get "new"
response.should be_success
end
end

Rails "POST" member route responds to all request types

I'm trying to set up a member route for my 'foo' controller, which is named 'bar' such that it should ONLY respond to post requests. However, I notice that in my RSpec tests it responds to ALL request types (GET, POST, PUT, DELETE).
I was under the impression that defining the route as so, would restrict it such that it would only respond to POST requests:
resources :foo do
member do
post 'bar'
do
end
This seems further confirmed by the fact that when I run rake routes it ONLY shows the 'bar' route like so:
bar_foo POST /foo/:id/bar(.:format) {:action=>"bar", :controller=>"foo"}
However, from RSPEC, the following test fails (meaning the controller processes request successfully) for GET, PUT, & DELETE:
describe FooController do
describe "GET bar" do
it "should not be successful" do
foo = FactoryGirl.create(:foo)
get :bar, :id => foo.id
response.should_not be_ok
end
end
end
Am I missing something small here? How do I restrict my 'bar' member route to only respond to "post" requests.
EDIT:
This appears to be an issue with either RSpec 2.0 or ActionController::TestCase, because I get the following error when I try to hit /foo/:id/bar on my sever with anything but POST:
Routing Error
No route matches [GET] "/foo/1/bar"
From my own experience it appears that RSpec controller tests will not attempt to enforce routing behavior as long as the route exists. RSpec does provide route testing, specifically the be_routable matcher.
Given the following route.rb snippet:
post :foo, to: 'foo#create'
it appears
it "won't work as expected" do
get :foo
expect(response).to be_ok #=> pass
post :foo
expect(response).to be_ok #=> pass
end
will pass. Only when the route is missing entirely from route.rb will it fail.
The following will pass and can be used to perform the test we're interested in:
it "responds to only the proper HTTP verbs" do
expect(get: :foo).not_to be_routable #=> pass - Cannot GET 🙅‍♂️
expect(post: :foo).to be_routable #=> pass - POST works 🎉
end

Unit testing a controller method that is called via a custom route

How do I unit test a controller method that is called via a custom route?
The relevant route is:
/auth/:provider/callback(.:format) {:controller=>"sessions", :action=>"create"}
On the spec for the SessionsController I can't just use get :create since that route doesn't exist. If I also use get /auth/facebook/callback/ it'll tell me that No route matches {:controller=>"sessions", :action=>"/auth/facebook/callback"}.
It also seems like I can't just use controller.create since #create accesses some keys from the request hash and it also redirects to another path, even if I set request.env['something'] in the spec file.
A functional test should test the function of each action
(given a set of parameters)
Crucially, you should keep your functional tests decoupled from your routes.
(else what's the point in the routing abstraction anyway)
In test::unit a functional test looks something like this
test "#{action_name} - does something" do
#{http_verb} :#{action_name}, :params => {:their => "values"}
assert_response :#{expected_response}
end
Note, we don't mention routing anywhere.
So a real example for your create
test "create - creates a session" do
get :create, :provider => "your provider"
assert_response :success
end
Rails will choke if it can't match a route to this request.
If this doesn't work I suggest you check two things
"get" is the correct http verb
there are no other required parameters in your route (I can see :provider is one)
If I'm doing anything wacky with routing,
I normally add a separate test.
test "create - routing" do
assert_recognizes({
:controller => "sessions",
:action => "create",
:provider => "yourProvider"
}, "/auth/yourProvider/callback")
end
As long as this matches up with your action test
all should be well.

Rails 3 and Rspec 2 namespaced controller problems

I;m trying to write a spec that tests a controller that is namespaced, let's call it Admin::FooController. My spec is called admin_foo_controller_spec.rb. In the spec, I'm trying to write a very basic spec to see if it can retrieve an action of a controller.
describe "GET 'index'" do
it "should be successful" do
get 'index'
response.should be_success
end
end
But instead I get an error:
Failure/Error: get 'index'
No route matches {:controller=>"admin/foo"}
For another action, I have basically the same test, and I get that the response is not successful. In the actual webapp, I can access these urls fine. One thing I should mention is that this isn't a RESTful resource (it makes no sense as one) so it's really just a group of related administrative actions, so in my routes.rb it's basically like
namespace :admin do
scope "foo" do
match "action1" => "foo#action1"
match "action2" => "foo#action2"
match "/index" => "foo#settings"
match "settings" => "foo#settings"
end
end
Any ideas what I'm doing wrong? It seems like namespacing is asking for trouble with rails, especially for a novice like me. Thanks!
In your route your have no index action in you Foo controller try to get 'settings'
describe "GET 'index'" do
it "should be successful" do
get 'settings'
response.should be_success
end
end
In a controller spec you need define the action of your controller not the real route. You try the routes in a integration test.

Resources