Scenario
I have a controller with two actions only - :create and :delete. Where the create action is defined thus:
def create
# some code...
if #thing.save
redirect_to :back, :notice => "Successfully created."
else
redirect_to :back, :notice => "Successfully deleted."
end
end
I link to the action using...
<%= link_to "Become a friend", things_path(...), :method => :post %>
...in the view.
Problem
This works fine as I interact with the app in my browser. However, I wish to test this functionality using RSpec integration testing using Webrat's helper method - click_link "Become a friend" - which I think is correct. But I get this error
Failure/Error: click_link "I like Person-1's taste"
AbstractController::ActionNotFound:
The action 'index' could not be found for ThingsController
I can create an empty index action in the Things controller but this would violate the KISS Principle.
Questions
How can I workaround/fix this? And are there any best practices for cases like this?
The problem is that :method => :post makes rails create a form which is then submitted when you click the link. This only works with javascript enabled which webrat does not support out of the box. Have a look at https://github.com/brynary/webrat/wiki and try to get the test running with selenium. As selenium actually uses a real browser, your specs should run.
Related
I'm trying to stub the model method which I'm using in my controller, but it never seems to be working. Can someone let me know the proper way to do it
User Controller
if current_user.user_token
#user = #account.users.find(params[:id])
#user.revoke_seat(:admin, current_user)
render :template => "/admin/users/revoke_seat"
else
render :js => "window.location.href='#{server_url}/oauth/authorize?response_type=code&client_id=#{client_id}&state=#{request.referrer}?auto_revoke_seat=true&redirect_uri=#{auth_service_callback_url}";
end
Rspec
before do
users(:admin).stub(:internal_admin?).and_return(true)
login_as :admin
user.stub(:user_token).and_return("123123")# THIS IS NOT WORKING
end
it "should redirect to authentication service to generate access token" do
expect(user).to receive(:user_token).and_return(true)
xhr :put, :revoke_seat, account_id: account.id, id: user.id
expect(response).to render_template('admin/users/revoke_seat')
expect(assigns(:account)).to eq(account)
expect(assigns(:user)).to eq(user)
end
You might try the allow approach instead of stub. E.g., allow(:admin).to receive(:internal_admin?).and_return(true)
I'm working on a training app which is an Ogame-Like game (https://github.com/arnlen/ogame-like).
I'm using rspec (with Capybara) in order to test my app.
I'm stacked for several hours because rspec is complaining for an error which *I can't reproduce * by myself with my browser.
Here is my rspec code :
describe 'Planet pages' do
let(:user){FactoryGirl.create(:user)}
before {sign_in user}
subject {page}
describe "new planet page" do
before {visit new_planet_path}
describe "with valid information" do
before do
visit new_planet_path
fill_in "Name", with: "MyPlanet"
click_button "Validate"
end
# This test doesn't pass
it {should have_selector('h1', text: "Planet")}
end
end
end
The failure :
1) Planet pages new planet page with valid information
Failure/Error: it {should have_selector('h1', text: "Planet")}
expected css "h1" with text "Planet" to return something
# ./spec/requests/planet_pages_spec.rb:34:in `block (4 levels) in <top (required)>'
Here is the involved code.
My function "sign_in" used by rspec (location : spec/support/utilities.rb)
def sign_in(user)
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
My UsersController
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :show, :edit, :update, :destroy]
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
redirect_to new_planet_path
else
render 'new'
end
[...]
My PlanetsController
class PlanetsController < ApplicationController
before_filter :signed_in_user
def index
#planets = current_user.planets
end
def new
#planet = Planet.new
end
def create
#planet = Planet.new(name: params[:planet][:name],
coordinates: generate_coordinates,
metal_ressource: 1000,
user_id: current_user.id)
if #planet.save
flash[:success] = "Welcome on your first planet!"
redirect_to action: 'index'
else
flash[:error] = "Error naming your planet"
render 'new'
end
end
end
And My Planet Index view
<% #planets.each do |planet| %>
<h1>Planet : <%= planet.name %></h1>
<p><%= "Coordinates : #{planet.coordinates}" %></p>
<% end %>
I tried to user the Capybara method "save_and_open_page", but rspec raised an error "undefined method"
I also tried step by step debugging by iterations on my spec file, and it revealed that the error occurs right after the "click_button 'Validate'". For an unknown reason, rspec seems not to be able to reach the planets_path ("index" action from PlanetsController).
I'm out, if anybody has an idea, I take it !
EDIT : SOLVED - Found the problem!
Using the "save_and_open_page" method from Capybara, I figured out what was going on: the planet created by rspec didn't have any coordinates, which was not allowed by the model.
How to debug with the wonderful "save_and_open_page" method
Add this to your gemfile : "gem 'launchy'"
Install it : bundle install
Put the command "save_and_open_page" wherever you want
Hope it could help. :)
Capybara also has a save_page method, which is easier to use as it does not seem to need the "launchy" gem. The pages are saved in tmp/capybara. In the rspec tests, be sure to use save_page inside before, it, or some other block. It will not work as a separate command. Example:
before { visit signup_path; save_page }
As I continue to learn my way around TDD with RSpec 2 and Rails 3.1, I can't seem to find a solution to this problem.
I have a Users controller with a new and create action. In my UsersController spec, I have
users_controller_spec.rb
describe "POST 'create'" do
before(:each) do
#attr = Factory.attributes_for(:user)
end
it "should assign an #user variable" do
post :create, :user => #attr
assigns[:user].should_not be_nil
assigns[:user].should be_kind_of(User)
end
end
and in my UsersController,
users_controller.rb
def create
#user = User.new(params[:user])
end
This spec is failing with
1) UsersController POST 'create' should assign an #user variable
Failure/Error: post :create, :user => #attr
ActionView::MissingTemplate:
I can continue to implement application code to get this test to pass, but I feel like this test should be passing as it is.
Any suggestions?
Your create method needs to do something. Either render a template or redirect. Since you're not telling it to redirect it's assuming that you want it to render a template but when it can't find a create.html.erb file it throws an error.
You're best bet is to do either this:
def create
#user = User.new(params[:user])
redirect_to root_url
end
or this:
def create
#user = User.new(params[:user])
render :nothing => true
end
To test rendering nothing you'll want:
expect(response).to render_template(nil)
I've come across this recently myself. It seems one possibility would be to rescue the error in your test.
it "should assign an #user variable" do
begin
post :create, :user => #attr
rescue ActionView::MissingTemplate
# This is okay because(/as long as) we test the render/redirect
# in a separate spec where we don't rescue this exception.
end
assigns[:user].should_not be_nil
assigns[:user].should be_kind_of(User)
end
I don't know how "correct" this solution is. On one hand, it definitely emphasizes the "testing one thing at a time" mentality, on the other, it seems kind of ugly.
Edit
I suppose you could make some nice wrappers in your spec helper, something like
def post?(action, params = {})
post action, params
rescue ActionView::MissingTemplate
end
I have in my User view Index page a button_to tag as follows:
<%= button_to "Make Admin", :action => :make_admin :user => user %>
In the User controller i have:
def make_admin
#user = User.find(params[:id])
#changed_user.role = 3
#changed_user.save
end
I get a message about bad routing, but since I'm not interested in changing the view until after the action I don't know how to route this action. Where have i gone wrong?
You need to name the path in your routes:
# routes.rb
get 'your_path' => 'user#make_admin, :as => 'make_admin' # can use post too
# controller
def make_admin
# logic to make an admin
redirect_to(some_other_path, :notice => 'User was made an admin')
end
then, in your view,
button_to 'make admin', make_admin_path
You might also want to make the call remotely, but you'll need to post another question with more information in that sense
I have an application, on which I have two user interfaces.
The first one is for normal users and the second one is for iphone users.
Everything was working fine until i refactored my code within controller to use the respond_with declarative instead of respond_to.
The application is still working for the html interface(:format => :html) but not on the iphone interface(:format => :iphone).
On the iphone, when I do the following action (:index, :new, :edit, :show) it works.
But when i do (:create, :update, :destroy), I get errors saying the template is not found(create.iphone.haml for example).
On my controller I have
respond_to :html, :iphone
And then for example, the edit and the update action
def edit
#refund = Refund.find(params[:id])
respond_with(#refund)
end
def update
#refund = Refund.find(params[:id])
if #refund.update_attributes(params[:refund])
flash[:notice] = 'Refund was successfully updated.'
end
respond_with(#refund, :location => project_refunds_path(#project))
end
In fact, I would like the :iphone format is handle as :html is ... and not by calling the to_format method as it is specified into the doc.
Solved it by myself.
Just need to add this to an initializer file :
ActionController::Responder.class_eval do
alias :to_iphone :to_html
end
What if you do:
respond_with(#refund, :location => project_refunds_path(#project)) do |format|
format.iphone { whatever you had here before refactoring }
end