Get & post methods in controller's test

I’m using RSpec (through the gem rspec-rails) for testing and developing
my application.
Then, I’ve tried to “test” a controller and run up against a something
that in last, I’m not understanding: post and get methods.
I’ve searched them in RoR doc, but their’s not documented.

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, while get a http get one, so I’ve writed:
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 load the login action and not the login_create! After a
lot of searching and trying, I’ve wrote:
post :login_create
… And it works as expected. However, after that I’ve tried:
get :login_create
… And this works also! O_o

So, what this kind of methods really do and how are intended to be used?

Your route syntax is confusing to me. How about:

controller :sessions do
match ‘/login’ => :login_create, :via => :post
match ‘/login’ => :login, :via => :get
match ‘/logout’ => :logout
end

…which is pieced together from here:

7stud – wrote in post #1074532:

Your route syntax is confusing to me. How about:

controller :sessions do
match ‘/login’ => :login_create, :via => :post
match ‘/login’ => :login, :via => :get
match ‘/logout’ => :logout
end

…which is pieced together from here:

ActionDispatch::Routing

Equals effect, but the one I’ve used is more succinct :slight_smile:
It also explained in “HTTP helper methods” paragraph of the linked api.

The problem isn’t in the route, but in the strange behavior of get and
post, instance methods of ActionController::TestCase::Behavior

Equals effect, but the one I’ve used is more succinct :slight_smile:

In no programming language is succinct better than clarity. In my brief
exposure to Rails, it suffers a lot from ZERO clarity. I’d rather be
clear than succinct.

The problem isn’t in the route, but in the strange behavior of get and
post, instance methods of ActionController::TestCase::Behavior

My rspec tests seem to work as you would expect. I used a controller
named Dogs to test the routes:

require ‘spec_helper’

describe “Dogs Pages” do

describe “POST to /login” do
it “returns http success” do
post ‘/login’
response.should be_success
response.should render_template ‘dogs/login_create’
response.body.should include(‘hello world’)
end
end

describe “GET to /login” do
it “should have h1 of ‘Doggie’” do
visit ‘/login’
page.should have_selector(‘h1’, text: “Login”)
end
end

end

If I change the ‘post’ to ‘get’, that test fails.

On Tuesday, September 4, 2012 12:56:05 AM UTC+1, Ruby-Forum.com User
wrote:

But this will load the login action and not the login_create! After a
lot of searching and trying, I’ve wrote:
post :login_create
… And it works as expected. However, after that I’ve tried:
get :login_create
… And this works also! O_o

Controller specs bypass your routes entirely (as part as action invocation
goes, they’re still used if you try to generate a url inside the
action).
If you do

get :action_name

then it will invoke that action with a get request, whether there’s a
route
for it or not.

post ‘/login’
fails because there are is no ‘/login’ action (but there is a ‘login’
one).

If you write an spec request spec (or a rails integration test) then
get/post are the rack test methods instead which do take actual paths.

Fred

So, what this kind of methods really do and how are intended to be used?

That’s strange °_° In my app it fails:

.F.

Failures:

  1. SessionsController POST to ‘login’ returns http success
    Failure/Error: post ‘/login’
    ActionController::RoutingError:
    No route matches {:controller=>“sessions”, :action=>"/login"}

    ./spec/controllers/sessions_controller_spec.rb:15:in `block (3

    levels) in <top (required)>’
    ====================================================================

That’s rake routes:

      login POST   /login(.:format)          sessions#login_create
            GET    /login(.:format)                sessions#login
     logout GET    /logout(.:format)               sessions#logout
      users GET    /users(.:format)                users#index
            POST   /users(.:format)                users#create
   new_user GET    /users/registrati(.:format)     users#new
  edit_user GET    /users/:id/edit(.:format)       users#edit
       user GET    /users/:id(.:format)            users#show
            PUT    /users/:id(.:format)            users#update
            DELETE /users/:id(.:format)            users#destroy

====================================================================

And this is the failing spec:

require ‘spec_helper’

describe SessionsController do
describe “GET ‘login’” do
before { get ‘login’ }

it "returns http success" do
  response.should be_success
end

end

describe “POST ‘login’” do
it “returns http success” do
post ‘/login’
response.should be_success
response.should render_template ‘sessions/login_create’
end
end

describe “GET ‘logout’” do
it “returns http success” do
get ‘logout’
response.should be_success
end
end
end

If I change post '/login' with post 'login' it fails with:

F…

Failures:

  1. SessionsController POST to ‘login’ returns http success
    Failure/Error: response.should render_template
    ‘sessions/login_create’
    expecting <“sessions/login_create”> but rendering with
    <“sessions/login, layouts/application”>

    ./spec/controllers/sessions_controller_spec.rb:17:in `block (3

levels) in <top (required)>’

By this error, it seems to me that post actually perform a get; really
strange @_@

7stud, which version of rails & rspec do you have? Mine:

  • rails (3.2.6)
  • railties (3.2.6)
  • rspec (2.11.0)
  • rspec-core (2.11.0)
  • rspec-expectations (2.11.1)
  • rspec-mocks (2.11.1)
  • rspec-rails (2.11.0)

One thing I’ve noted in your spec is that in the outermost describe you
pass a string. If I do that, rspec complains about uninitialized
@controller; if I move the spec in spec/ dir (actually it’s in
spec/controllers/), the post method isn’t found @@
What’s wrong with me? .
.

Frederick C. wrote in post #1074851:

On Tuesday, September 4, 2012 12:56:05 AM UTC+1, Ruby-Forum.com User
wrote:
Controller specs bypass your routes entirely (as part as action invocation
goes, they’re still used if you try to generate a url inside the
action).
If you do

get :action_name

then it will invoke that action with a get request, whether there’s a
route
for it or not.

post ‘/login’
fails because there are is no ‘/login’ action (but there is a ‘login’
one).

If you write an spec request spec (or a rails integration test) then
get/post are the rack test methods instead which do take actual paths.

Fred

If I’m getting what you say, in a controller spec it’s not really
important if I use get, post, delete or put methods - unless the action
itself has some logic affected by the request type, right? :slight_smile:

On Wednesday, September 5, 2012 10:50:15 PM UTC+1, Ruby-Forum.com User
wrote:

If I’m getting what you say, in a controller spec it’s not really
important if I use get, post, delete or put methods - unless the action
itself has some logic affected by the request type, right? :slight_smile:

Correct, although it would be more than a little perverse not to use the
request type you expect.

Fred

Sorry, my tests were inside an integration or ‘request’ test:

/spec/requests/dog_pages_spec.rb

And to add to what Frederick C. said, in my rails tutorial book it
says that inside a controller test, you can’t use urls e.g:

get ‘/about’
get ‘/’

…you can only do:

get ‘actionA’
get ‘actionB’

On the other hand, integration tests allow you to test your whole app
end to end, and therefore you can use urls to simulate what users do.

My routes are the same as yours:

$ rake routes
login POST /login(.:format) dogs#login_create
GET /login(.:format) dogs#login
logout /logout(.:format) dogs#logout


Ok, thanks to both of you for clearing my mind :slight_smile: