assert_redirected_to usage? - ruby-on-rails

Just starting out in using Test::Unit but having a problem nailing down a redirect test:
test "should destroy line_item" do
assert_difference('LineItem.count', -1) do
delete :destroy, id: #line_item
end
assert_redirected_to controller: 'carts', action: 'show'
end
which throws the following error:
Failure: Expected response to be a redirect to <http://test.host/carts/980190962> but was a redirect to <http://test.host/carts/980190963>.
test_should_destroy_line_item(LineItemsControllerTest)
test/functional/line_items_controller_test.rb:47:in `block in <class:LineItemsControllerTest>'
The docs state the following:
assert_redirected_to(options = {}, message=nil) public
Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that assert_redirected_to(:controller => "weblog") will also match the redirection of redirect_to(:controller => "weblog", :action => "show") and so on.
but assert_redirected_to controller: 'carts' leads to an even more outright failure:
Failure: Expected response to be a redirect to <http://test.host/carts> but was a redirect to <http://test.host/carts/980190963>.
test_should_destroy_line_item(LineItemsControllerTest)
If the documentation is correct, what am I missing? If it is not, what alternatives do I have to test the redirect regardless of a matching id?

It appears that the documentation for assert_redirected_to is not entirely correct, but part of the problem had to do with the cart_id being different at the setup stage (when #line_item is created) as compared to the stage where it is destroyed. So the workaround is to modify the fixture/controller such that it will have the same cart_id at the time of destruction. It doesn't solve the real problem with assert_redirected_to but at least the test will pass.

Related

Rails 5 test doesn't reproduce actual behaviour on PATCH

I'm really stuck here. I have a Language model, that gets updated in this method:
def update
#language = Language.find(params[:id])
if #language.update_attributes(language_params)
flash[:success] = 'Language information updated.'
redirect_to #language
else
#skill_errors = #language.errors
render 'edit'
end
end
The intended behaviour for a successful update reproduces when I run it on my local server, object gets updated, flash appears and redirect to #language happens.
In the test, however, I only get the 200: Success response, and the object doesn't get updated. Here's the test code:
test 'should allow update when logged in as admin user' do
sign_in(#admin)
patch language_path(#ruby_language), params: { language: { name: 'Test'} }
assert_not flash.empty?
assert_redirected_to #ruby_language
#ruby_language.reload
assert_equal 'Test', #ruby_language.name
end
#admin and #ruby_language are defined in fixtures. All the asserts in this test fail, including the last one, with reload. My guess is that there might be some routing glitch caused by my usage of Devise and/or Kaminari gems? On the other hand, my Language routes are very simple:
resources :languages, concerns: :paginatable (the concern is here for human-readable URL formatting). Please, keep in mind that everything works as intended, only tests fail for some reason... Thanks in advance!
It turned out I was simply missing a required parameter in my update hash. Used Arun Kumar's tip of adding puts #response.body after patch.

Route Generation error for :create action in Minitest

I am currently writing functional tests for all of my controllers. For every single one, I can't get the create action to work. I keep getting the following error when I run rake test:
ActionController::UrlGenerationError: No route matches {:action=>"create", :comment=>{:content=>"I'm a comment.", :product_id=>"685617403"}, :controller=>comments}
Here is the action I am trying to test:
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
#product =Product.find(params[:product_id])
#comment=Comment.create(params[:comment].permit(:content))
#comment.user_id= current_user.id
#comment.product_id= #product.id
if #comment.save
redirect_to product_path(#product)
else
render 'new'
end
end
end
Here is the route:
POST /products/:product_id/comments(.:format) comments#create
The following is my test:
def setup
#user=users(:chris)
#product=products(:banana)
end
test "should redirect destroy action if not signed in" do
post :create, comment: { content: "I'm a comment.", product_id:#product.id}
assert_redirected_to new_user_session_path
end
I can't figure out what I am doing wrong. I am fairly certain I am passing the correct params in.I've also tried it with and without a user_id: param and it still throws the same error. It works fine on the app, I've called params in the web console when making the request and it matches what I am passing. The only thing I am missing is the :id but I assumed that would be assigned when the comment was created in the test. Also there are no unique constraints which would prevent the created comment from being valid. My associations are in place and when it works on the app it saves to the database with the user_id and product_id. What am I missing?
I think you need to put product_id as its own first-level param too in order for the route to match up correctly. So try this instead:
post :create, product_id: #product.id, comment: { content: "I'm a comment.", product_id: #product.id }
Note that in your controller action, you reference params[:product_id] directly already, you don't reference params[:comment][:product_id]. Then, to reduce duplication, you can create the Comment as a child of that Product in your controller:
#comment = #product.comments.create(other params...)
Rails' routing errors can be extremely vague and unhelpful. 90% of the time the error boils down to some variation of this: a mismatched or misnamed ID, a nil value, etc.

Understanding Rails 3's respond_with

Utilizing ActionController's new respond_with method...how does it determine what to render when action (save) is successful and when it's not?
I ask because I'm trying to get a scaffold generated spec (included below) to pass, if only so that I can understand it. The app is working fine but, oddly, it appears to be rendering /carriers (at least that's what the browser's URL says) when a validation fails. Yet, the spec is expecting "new" (and so am I, for that matter) but instead is receiving <"">. If I change the spec to expect "" it still fails.
When it renders /carriers that page shows the error_messages next to the fields that failed validation as one would expect.
Can anyone familiar with respond_with see what's happening here?
#carrier.rb
validates :name, :presence => true
#carriers_controller.rb
class CarriersController < ApplicationController
respond_to :html, :json
...
def new
respond_with(#carrier = Carrier.new)
end
def create
#carrier = Carrier.new(params[:carrier])
flash[:success] = 'Carrier was successfully created.' if #carrier.save
respond_with(#carrier)
end
Spec that's failing:
#carriers_controller_spec.rb
require 'spec_helper'
describe CarriersController do
def mock_carrier(stubs={})
(#mock_carrier ||= mock_model(Carrier).as_null_object).tap do |carrier|
carrier.stub(stubs) unless stubs.empty?
end
end
describe "POST create" do
describe "with invalid params" do
it "re-renders the 'new' template" do
Carrier.stub(:new) { mock_carrier(:save => false) }
post :create, :carrier => {}
response.should render_template("new")
end
end
end
end
with this error:
1) CarriersController POST create with invalid params re-renders the 'new' template
Failure/Error: response.should render_template("new")
expecting <"new"> but rendering with <"">.
Expected block to return true value.
# (eval):2:in `assert_block'
# ./spec/controllers/carriers_controller_spec.rb:81:in `block (4 levels) in <top (required)>'
tl:dr
Add an error hash to the mock:
Carrier.stub(:new) { mock_carrier(:save => false,
:errors => { :anything => "any value (even nil)" })}
This will trigger the desired behavior in respond_with.
What is going on here
Add this after the post :create
response.code.should == "200"
It fails with expected: "200", got: "302". So it is redirecting instead of rendering the new template when it shouldn't. Where is it going? Give it a path we know will fail:
response.should redirect_to("/")
Now it fails with Expected response to be a redirect to <http://test.host/> but was a redirect to <http://test.host/carriers/1001>
The spec is supposed to pass by rendering the new template, which is the normal course of events after the save on the mock Carrier object returns false. Instead respond_with ends up redirecting to show_carrier_path. Which is just plain wrong. But why?
After some digging in the source code, it seems that the controller tries to render 'carriers/create'. There is no such template, so an exception is raised. The rescue block determines the request is a POST and there is nothing in the error hash, upon which the controller redirects to the default resource, which is the mock Carrier.
That is puzzling, since the controller should not assume there is a valid model instance. This is a create after all. At this point I can only surmise that the test environment is somehow taking shortcuts.
So the workaround is to provide a fake error hash. Normally something would be in the hash after save fails, so that kinda makes sense.

rspec rails mocking session hash

I am trying to mock out the session hash for a controller like so:
it "finds using the session[:company_id]" do
session.should_receive(:[]).with(:company_id).and_return 100
Company.should_receive(:find).with(100)
get 'show'
end
When I call get 'show' it states:
received :[] with unexpected arguments
expected: (:company_id)
got: ("flash")
The controller code looks like:
def show
company_id = session[:company_id]
#company = Company.find params[company_id]
end
I have also simply tried setting
it "finds using the session[:company_id]" do
session[:company_id]= 100
Company.should_receive(:find).with(100)
get 'show'
end
but then get an issue about:
expected: (100)
got: (nil)
Anyone have ideas why?
I just ran into this. I couldn't manage to get should_receive to not interfere with the flash stuff.
But this let me test the behavior I was looking for:
it "should redirect to intended_url if set" do
request.env['warden'] = double(:authenticate! => true)
session.stub(:[]).with("flash").and_return double(:sweep => true, :update => true, :[]= => [])
session.stub(:[]).with(:intended_url).and_return("/users")
post 'create'
response.should redirect_to("/users")
end
Hope that helps...
I could not figure out how to mock the session container itself, however in most cases simply passing session data with request should be enough. So the test would split into two cases:
it "returns 404 if company_id is not in session" do
get :show, {}, {}
response.status.should == 404 # or assert_raises depending on how you handle 404s
end
it "finds using the session[:company_id]" do
Company.should_receive(:find).with(100)
get :show, {}, {:company_id => 100}
end
PS: forgot to mention I'm using some customized helpers from this snippet.
try this:
session.expects(:[]).with(has_entries('company_id' => 100))
It's because you fetch flash session from your controller. So define it. Flash is save in session.
it "finds using the session[:company_id]" do
session.stub!(:[]).with(:flash)
session.should_receive(:[]).with(:company_id).and_return 100
Company.should_receive(:find).with(100)
get 'show'
end

Why is this controller test on a create action failing?

I'm getting a failing test here that I'm having trouble understanding. I'm using Test::Unit with Shoulda enhancement. Action in users_controller.rb I'm trying to test...
def create
unless params[:user][:email] =~ / specific regex needed for this app /i
# ...
render :template => 'sessions/new'
end
end
Test...
context 'on CREATE to :user' do
context 'with invalid email' do
setup { post :create, { 'user[email]' => 'abc#abcd' } }
should_respond_with :success
end
# ...
end
Fails because "response to be a <:success>, but was <302>". How is it 302?
Change action to...
def create
render :template => 'sessions/new'
end
Test still fails.
#Ola: You're wrong: POST is connected to create. PUT is normally connected to update.
A :forbidden is quiet odd though. Here are some suggestions to find the problem (I've never used Shoulda, but I don't think it is a problem with Shoulda.
Make sure the route is defined in config/routes.rb and check with rake routes
Do you have any before_filters that could be responsible for that behaviour (login filter, acts_as_authenticated etc..)? Checkout log/test.log. A halt in the filter chain shows up there.
Print out the response body puts response.body to see what you get returned.
Hope this helps.
If you're using default REST-ful URLs, you probably should use PUT, not POST... Since PUT is connected to create, POST to that URL will give you an unauthorized and redirect.

Resources