I want to test a destroy action that I know to be working, but I want a test for it.
It is a destroy action.
The route:
DELETE /journals/:id(.:format) journals#destroy
In the admin/journals.rb registration block have a controller block to override the destroy method.
ActiveAdmin.register Journal do
controller do
def destroy
# do stuff and redirect.
end
end
end
My test
require 'rails_helper'
RSpec.describe JournalsController, type: :controller do
let!(:journal) { Journal.create(title: 'Title1') }
it 'sets the out_of_stock attribute to true' do
expect {
delete :destroy, params: { id: journal.id }
}.to change { journal.reload.out_of_stock }.from(false).to(true)
end
end
For some reason this does not hit the method, nor does it pass.
Result
1) JournalsController destroy journal is in stock and receives destroy sets the out_of_stock attribute to true
Failure/Error:
expect {
delete :destroy, params: { id: journal.id }
}.to change { journal.reload.out_of_stock }.from(false).to(true)
expected `journal.reload.out_of_stock` to have changed from false to true, but did not change
# ./spec/controllers/journals_controller_spec.rb:15:in `block (4 levels) in <top (required)>'
If I override #show in the same manner and call it from a spec with
get :show, params: {id: journal.id }
it does hit the action inside the registration block.
I have tried other text book examples of controller transactions like creating a user, but it just doesn't work.
I do however have an api namespace where tests pass. It only calls get :index though.
Is there some special AA rspec settings that I don't have? Maybe in spec_helper/rails_helper.
How do I make my test hit my action?
Any help is appreciated.
It turns out I hadn't taken some permission checks into account and the test got redirected. Setting up a current_user with the correct role (in my case) fixed the issue. The destroy action was ultimately called.
Related
I want to test letsrate generated controller.
But I don;t know how do this, because I can not understand how it works.
rater_controller.rb
class RaterController < ApplicationController
def create
if user_signed_in?
obj = params[:klass].classify.constantize.find(params[:id])
obj.rate params[:score].to_i, current_user, params[:dimension]
render :json => true
else
render :json => false
end
end
end
UPDATE
Letsrate is a gem for rails
rater_controller_spec.rb
require 'rails_helper'
describe RaterController do
describe 'POST create' do
let(:valid_attributes)do {
klass: 'Hotel',
dimension: 'rating',
score: '5'
}
end
it 'user signed in' do
user = create(:user)
hotel = create(:hotel)
post :create, { rate: valid_attributes, rater_id: user.id, rateble_id: hotel.id }
sign_in user
end
end
end
The source code you posted makes it pretty obvious how it works. You need to call the create action in RaterController with these params: klass, id, score, dimension. Let's say the klass param is "Restaurant", which is also the name of an ActiveRecord model class. The controller will query the database for a restaurant with the specified ID. Then it will call the rate method on that object with the specified parameters, which presumably inserts a row into the database representing the user's new rating. To test it, you could simply call the controller action and then check to make sure the row got added to the database.
I'm testing to make sure that a created user is assigned to my instance variable #user. I understand what get means, but I'm not sure what to write for the test. I'm returning with an argument error for a bad URI or URL. What's wrong with my test and how do I fix it?
it "checks #user variable assignment for creation" do
p = FactoryGirl.create(:user)
get :users
# I'm confused on what this line above means/does. What does the hash :users refer
#to
assigns[:user].should == [p]
end
The expected URI object or string error refers to get :users and the error is as follows
Failure/Error get :users
ArgumentError:
bad argument: (expected URI object or URI string)
I guess that what you want is
it "checks #user variable assignment for creation" do
p = FactoryGirl.create(:user)
get :show, id: p.id
assigns(:user).should == p
end
The line you were not sure about checks that content of the assigned variable (#user) in the show view of the user p, is equal to the p user you just created more information there
what action are you trying to test? usually, for creation, you need to test that the controller's "create" action creates a user and assigns an #user variable
I would test it this way:
describe 'POST create' do
it 'creates a user' do
params = {:user => {:name => 'xxx', :lastname => 'yyy'}}
User.should_receive(:create).with(params)
post :create
end
it 'assigns the user to an #user instance variable' do
user = mock(:user)
User.stub!(:create => user)
post :create
assigns(:user).should == user
end
end
notice that I stub/mock all user methods, since you are testing a controller you don't have to really create the user, you only test that the controller calls the desired method, the user creation is tested inside the User model spec
also, I made 2 tests (you should test only 1 thing on each it block if possible, first it test that the controller creates a user, then I test that the controller assigns the variable
I'm assuming your controller is something like this:
controller...
def create
#user = User.create(params[:user])
end
which is TOO simple, I guess you have more code and you should test that code too (validations, redirects, flash messages, etc)
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
I'm using RSpec + Shoulda to test my RESTful controller in Rails 3. I'm having trouble figuring out how to test the create action's redirect. The standard RESTful controller should redirect to the show action for the new post. For example, if I have a ProjectsController for a Project model, then upon successful create, that action should:
redirect_to project_url(#project)
Shoulda provides a handy redirects_to macro for handling this. Here is what I have tried:
describe ProjectsController, '#create' do
context "Anonymous user" do
before :each do
#attrs = Factory.attributes_for(:project_with_image)
post :create, :project => #attrs
end
it { should assign_to(:project) }
it { should respond_with(:redirect) }
it { should redirect_to(#project) }
end
end
(Yes, I'm using FactoryGirl, but since I'm only using it for attributes in this case, it shouldn't matter. I think.)
How do I specify the last test there? It should redirect_to(...) what? I've tried #project, project_url(#project).. But I can't figure it out.
Looking at the Shoulda matcher code, I noticed that the redirect_to matcher can accept a block. But I'm not sure how to access the newly created #project object in that block...
Any thoughts?
Haven't tried it, but the problem probably is, that #project is not available in your spec. How about it {should redirect_to(Project.last) } or it {should redirect_to(assigns(:project)) }?
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.