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.
Related
Edit 2: OMG I AM SO STUPID. In my spec I have a let(:response) {MyModel.create()} so thats why its failing. Going to delete post
(edited for clarity)
In my routes file
root "search_email#index"
get "search_email/retrieve_last_user_survey" => "search_email#retrieve_last_user_survey"
Controller
class SearchEmailController < ApplicationController
def retrieve_last_user_survey
render :json => "")
end
end
Spec file
require "rails_helper"
RSpec.describe SearchEmailController, type: :controller do
describe 'GET #retrieve_last_user_survey' do
before do
get :retrieve_last_user_survey, :params => { :email => 'abc#abc.com'}
end
it "returns http success" do
expect(response).to have_http_status(:success)
end
end
end
When try to run my test, i get this
Failure/Error: expect(response).to have_http_status(:success)
expected a response object, but an instance of Relational::Response (custom model name) was received
I have no idea why I am not getting a response object, I know I am hitting the controller method cause I inserted puts and I can see it.
Also on a semi related note. If i create a button that hits this route. why does it redirect me to a show route. I thought it would just return some http request that i can see in the dev console. I know cause said I dont have a show route or a show template.
It's not meant to be facetious, but to get the test to pass, replace the render line in the controller with:
head :ok
Does the test pass? Probably. So now add some expectation on the content header, and then finally the content itself.
If you break it down into small pieces, you should find the problem. It's not obvious from what you've shared, we can't see into the controller method.
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.
I'm writing controller tests in Rails and RSpec, and it seems from reading the source code of ActionController::TestCase that it's not possible to pass arbitrary query parameters to the controller -- only routing parameters.
To work around this limitation, I am currently using with_routing:
with_routing do |routes|
# this nonsense is necessary because
# Rails controller testing does not
# pass on query params, only routing params
routes.draw do
get '/users/confirmation/:confirmation_token' => 'user_confirmations#show'
root :to => 'root#index'
end
get :show, 'confirmation_token' => CONFIRMATION_TOKEN
end
As you may be able to guess, I am testing a custom Confirmations controller for Devise. This means I am jacking into an existing API and do not have the option to change how the real mapping in config/routes.rb is done.
Is there a neater way to do this? A supported way for get to pass query parameters?
EDIT: There is something else going on. I created a minimal example in https://github.com/clacke/so_13866283 :
spec/controllers/receive_query_param_controller_spec.rb
describe ReceiveQueryParamController do
describe '#please' do
it 'receives query param, sets #my_param' do
get :please, :my_param => 'test_value'
assigns(:my_param).should eq 'test_value'
end
end
end
app/controllers/receive_query_param_controller.rb
class ReceiveQueryParamController < ApplicationController
def please
#my_param = params[:my_param]
end
end
config/routes.rb
So13866283::Application.routes.draw do
get '/receive_query_param/please' => 'receive_query_param#please'
end
This test passes, so I suppose it is Devise that does something funky with the routing.
EDIT:
Pinned down where in Devise routes are defined, and updated my example app to match it.
So13866283::Application.routes.draw do
resource :receive_query_param, :only => [:show],
:controller => "receive_query_param"
end
... and spec and controller updated accordingly to use #show. The test still passes, i.e. params[:my_param] is populated by get :show, :my_param => 'blah'. So, still a mystery why this does not happen in my real app.
Controller tests don't route. You are unit-testing the controller--routing is outside its scope.
A typical controller spec example tests an action:
describe MyController do
it "is successful" do
get :index
response.status.should == 200
end
end
You set up the test context by passing parameters to get, e.g.:
get :show, :id => 1
You can pass query parameters in that hash.
If you do want to test routing, you can write routing specs, or request (integration) specs.
Are you sure there isn't something else going on? I have a Rails 3.0.x project and am passing parameters.. well.. this is a post.. maybe it's different for get, but that seems odd..
before { post :contact_us, :contact_us => {:email => 'joe#example.com',
:category => 'Category', :subject => 'Subject', :message => 'Message'} }
The above is definitely being used in my controller in the params object.
I am doing this now:
#request.env['QUERY_STRING'] = "confirmation_token=" # otherwise it's ignored
get :show, :confirmation_token => CONFIRMATION_TOKEN
... but it looks hacky.
If someone could show me a neat and official way to do this, I would be delighted. Judging from what I've seen in the source code of #get and everything it calls, there doesn't seem to be any other way, but I'm hoping I overlooked something.
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.
I have a check_user_access_control before_filter in my ApplicationController that checks the logged user's roles and permissions before it lets him through. I am trying to write some tests on it and I can't find a good way of doing it.
For simple index actions I simply do:
it "allows access to mod" do
login_as(Factory(:mod)) # this is a spec helper
get :index
response.code.should == "200"
end
and it works just fine. For edit/show/create and other actions that need some params, interactions with the database and possible redirect after they run, it needs too many other stuff to be stubbed.
Is there a way to test if a specific action has been called after the before_filters? I am looking for something like controller.should_receive(:action_name) (which doesn't work) to replace the response.code.should == "200" line with.
versions: rails 3.0.4 and rspec 2.5
I tried another approach. We have a method in ApplicationController called redirect_to_login that I am now checking for with controller.should_receive(:redirect_to_login) and works.
While it detects correctly if the user is allowed or not, it stubs the method, which means that the controller action is run whether or not the user is allowed. Moreover the action depends on params and database and we don't want that.
If now I stub the action method with controller.stub!(:action_name), the action is not run but RSpec is still looking for the template. Well, some actions don't have templates, they just end with a redirect_to :action => :somewhere_else or render :text => "foobar" which at this point we don't care about.
In sort, what I need now is to find a way to make RSpec NOT worry about the template's existence.
When stubbing, you could still give a dummy implementation. Inside that implementation you could then raise an error, to make sure all execution is halted, or you do a redirect anyway.
E.g.
controller.should_receive(:redirect_to_log) { redirect_to login_url }
or
controller.should_receive(:redirect_to_log) { raise StandardError.new('login error') }
expect { get :index }.to raise_error
For more information check out the awesome rspec documentation.
Hope it helps.
To extend #nathanvda's answer:
When stubbing, you could still give a dummy implementation. Inside that implementation [...] do a redirect anyway.
You need to specify controller in the block:
expect(controller).to receive(:redirect_to_log) { controller.redirect_to login_url }
RSpec has a matcher that is also called redirect_to that takes precedence when looking up the method. Calling it directly on the controller works around that.
Final solution, with thanks to nathanvda:
it "allows access to moderator" do
login_as(Factory(:mod))
controller.stub!(action) { raise "HELL" }
controller.should_not_receive(:redirect_to_login)
expect { get action }.to raise_error(/HELL/)
end
it "denies access to user" do
login_as(Factory(:user))
controller.should_receive(:redirect_to_login) { raise "HELL" }
expect { get :index }.to raise_error(/HELL/)
end
posted on https://gist.github.com/957565