I am trying to implement some integration tests for my application to test a voting system I have in place but have run into some problems. First off, here is the test code I am trying to get to pass:
describe "vote_up_user" do
it "should update the user rating" do
click_link "user_up_arrow"
response.should have_selector("#user_rating", :content => "1")
end
end
Here is the link that gets clicked:
<%= link_to image_tag("uparrowbig.png"), vote_up_user_path(#user), :method => :post,
:id => "user_up_arrow", :class => "arrow", :remote => true %>
The corresponding action:
respond_to :html, :js
def vote_up_user
#voted_on_user = User.find(params[:id])
current_user.vote_exclusively_for(#voted_on_user)
respond_with(#voted_on_user, :location => user_path(#voted_on_user))
end
and in case anyone is interested the corresponding votes/vote_up_user.js.erb:
$("user_rating").update('<%= #voted_on_user.plusminus.to_s %>')
$("user_up_arrow").update('<%= image_tag("uparrowbigselect.png") %>')
$("user_down_arrow").update('<%= image_tag("downarrowbig.png") %>')
My problem is that I keep failing at the click_link line with the following error:
Missing template votes/vote_up_user with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:html], :locale=>[:en, :en]}
I can understand why this is failing as I do not have a template that is html.erb in the specified path. I actually have a js.erb file instead as this is an AJAX call, but this is not included in the :formats array and thus is not found. My question is then, what would be the best way to ensure that the :js format is search for when the integration test clicks on the link? Is this something I can simply adjust in the test or will I need to add it to the link helper?
I suspect the reason it's not working is that RSpec alone can't test Javascript.
When you add remote => true to a link, it only adds data-remote="true" as an attribute of the link, which doesn't mean anything without Javascript. That's why you see in your error :formats=>[:html]. It is only going to look for html views. In order for Rails to request the .js.erb view by default, you either need to have the .js on the end of the URL that it is requesting or actually use Javascript to request the page.
To get Javascript to actually run in your tests, you need to use something like Capybara. When you run your test, you'll actually see your browser start up and it will run your test actually in the browser.
If this is what you want to do, I would recommend watching Ryan Bates' recent Railscast: http://railscasts.com/episodes/257-request-specs-and-capybara
Update based on comments
respond_with will only redirect to the location you specify on POST, PUT, or DELETE requests. While you have :method => :post in the link, links will always generate GET requests when Javascript is disabled (since you're not using AJAX). Without Javascript, the only way to generate a POST request is with a form tag.
If you want it to degrade gracefully in this situation, you should either create an html view for these situations or put a block after the respond_with like this:
respond_with(#voted_on_user, :location => user_path(#voted_on_user)) do |format|
format.html { redirect_to user_path(#voted_on_user) }
end`
Related
I have an interesting situation. I am testing the following simple create action:
# will only be accessed via Ajax
def create
click = Click.new(params[:click])
click.save # don't really care whether its success or failure
end
Then I have the following very simple controller spec:
require 'spec_helper'
describe ClicksController, "creating a click" do
it "should create a click for event" do
xhr :post, :create, :click => {:event_id => 1}
# more test to come...
end
end
Seems trivial, yet I get the following:
Missing template clicks/create
Any tips would be appreciated.
Add to the controller action:
render :nothing => true
This one will automatically create the appropriate server's respone. More here
You will get this error if your controller renders only JSON or XML, yet you don't specify a format in the spec; your request then defaults to unsupported HTML. In that case, simply specify the supported format when you invoke the controller method from your spec. For example, change this:
post :create, registration: #user_hash
to this:
post :create, registration: #user_hash, format: :json
If you do not render anything in a controller action, rails will attempt to default to rendering a template (in this case clicks/create). I'd suggest rendering back at least a success message like so:
render :json => {:success => true}
Building on megas's answer, if you're looking to test a controller action that's only accessed via a UJS link and only has a .js.erb template, I'd put this in the controller to avoid breaking your UJS functionality:
respond_to do |f|
f.html { render nothing: true } # prevents rendering a nonexistent template file
f.js # still renders the JavaScript template
end
This will enable you to call the controller action by simply calling ActionController::TestCase::Behavior's get/post/put/delete methods instead of needing to call xhr, because it will successfully call the method, render nothing, and continue on, while leaving your UJS behavior intact.
I get this when I try and run my view:
Missing template application/login with {:formats=>[:html], :locale=>[:en], :handlers=>[:coffee, :erb, :builder]}. Searched in: * "/home/carladessi/Goods In Final/app/views"
in my controller I have:
def login
# respond_to do |format|
# format.html
end
and in my routes I have:
match "/login/", :controller => 'application', :action => 'login'
I'm guessing I need to put something else in the controller I just don't know what.. sorry if this is a really blatant question!
Restarted the server and it works fine !
It is not really conventional Rails to render views from the application_controller.
However, what is happening is Rails is looking for an actual template or view to be here:
RAILS_ROOT/app/views/application/login.html.erb
What you can do is add/create that template at the above path. Or you can redirect to another controller (which exists and does render an actual template).
Is there any method that i should look at in rails3.2 source code so as to know where the navigation or the url part of the render call get resolved?
The reason is, i have a small app in which url is of the form
www.example.com/bob/edit
the above route as it suggests renders the edit form.EDIT: i was able to get to this route by modifying response on the link_to helper.
def update
#when validation passes
redirect_to #user
#when validation fails
respond_to do |format|
format.html {render :action => "edit"}
end
end
Now the problem is when a validation error occurs on submission to update action of users_controller,
the url becomes
www.example.com/users/bob/edit
config/routes.rb
get "users/new", to: => "users#new"
resources :users
as you can see there's nothing interesting happening in routes,
in models/user.rb
def to_param
"#{name}"
end
in views/edit.html.erb
form_for(#user) do |f|
end
Observation: here when the form is rendered afresh, form 'action' points to "users/bob" but when the form is re-rendered 'cos of validation error, form action mysteriosly changes to "users/" which is weired and if i remove the to_param in user.rb model it works fine
Though its not such a big deal, i was thinking where, if i needed to override the url that is generated on render call, to change?????
Any suggestions and pointers to explore are wecome....
I'm not sure how you're getting the URLs you're getting, but a general answer to your question would be it doesn't. The URL you see after sending a request is the URL the request was sent to (or redirected to), not that of the page you came from, nor that of the template you render in the end. In your case, I'm guessing the problem is that you created a custom URL for the edit page, but not for update, and your form_for(#user) is sending the request to your update URL (probably PUT "/users/bob").
To fix this, the first thing is to create your custom update route. Maybe something like:
put ":id/update", to: => "users#update"
And then have your form_for use that URL:
form_for(#user, :url => "#{#user.to_param}/update")
I figured I should finally write some tests for my rails app.
My controller is "UsersController". It doesn't have any HTML as I just have an iphone app sending a post in to a rails controller.
Here is my test:
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def test_create
# to http post
# /users
#user[email]=%#&user[password]=%#&user[password_confirmation]=%#
#post
post(:create, :user => {:password => "testpassword", :password_confirmation => "testpassword"})
end
Problem is that I get this error:
1) Error:
test_create(UsersControllerTest):
ActionView::MissingTemplate: Missing template users/new with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:html], :locale=>[:en, :en]} in view paths
So I guess it's trying to populate an HTML page? If so, I find this odd. I would think it would directly do the post to the controller. Can someone confirm that this "post" method tries and populates an HTML form?
If this is the case, how should I proceed in writing a test to directly send an HTTP post to the controller?
Thanks for any help
You can specify "format" to make it work:
post(:action, {'param1'=>'value1', 'param2' => 'value2', :format => 'js'})
Unless you tell it otherwise the post method assumes the requested content type is HTML. Typically the create action looks something like this:
#user = User.new(params[:user])
if #user.save
redirect_to posts_path
else
render :new
end
If the action fails it tries to render 'users/new', which doesn't exist, thus the error.
So there are a couple of issues here. It's not clear what content type is expected (XML?). The action is failing, but we can't see why. It might help to edit the question to show us what the controller action does.
I don't really understand the pro's and con's of using "post" vs "get" vs "put" requests, on custom controller actions, and whether to use links or forms/buttons.
So let's say I have a simple to-do list with tasks, and a tasks controller, and I want a "complete" action where I find a specific task in the db and update it's status attribute from "incomplete" to "complete."
def complete
#task = Task.find(params[:id])
if #task.update_attributes(:status => "complete")
redirect_to tasks_url, :notice => "Completed!"
else
redirect_to tasks_url, :error => "Whoops."
end
end
What's the best practice way to define this route, which HTML request method should I use (post? put? get?), and should I use a plain link or a form? (and note: assume my user security model is all figured out with devise, and appropriate before filters, etc.)
And most of all, how would I articulate all this in a Rails 3 routes.rb file?
Note, the below code wasn't really working for me:
#routes.rb
resources :tasks do
members do
post 'complete'
end
end
so currently I'm using this instead:
#routes.rb
match 'tasks/:id/complete', 'tasks#complete', :as => "complete_task"
#view
= link_to "Complete", complete_task_path(:id => #task.id)
But this triggers a get request, and I feel like it should be a "put" or a "post." Or should it be a link at all? Should it be a form with hidden fields?
"link_to" method usually generates an anchor tag ie "<a></a>", ie a regular GET request
to do a POST request using link_to you should do the following
= link_to "Complete", complete_task_path(:id => #task.id), :method => :post
Remember if javascript is disabled in the browser, the above statement will fall back to a GET request instead of POST.