Rspec test fails while rendering - ruby-on-rails

For my user controller, while running the test cases I am facing render issue. It is redirecting to http://test.host/sign_in instead of rendering new.
Controller code
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
UserMailer.registration_confirmation(#user).deliver_now
session[:user_id] = #user.id
format.html { redirect_to sign_in_path, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
spec/users_controller_spec.rb
describe '#create' do
it 'User created' do
new_user = FactoryBot.build :user
post :create, params: {
user: {
id: new_user.id,
firstname: new_user.firstname,
email: new_user.email,
password: "s",
password_confirmation:"s"
}
}
expect(response).to render_template("new")
end
end
While running this test case I am facing this error!
Error
Failures:
1) UsersController controller test cases #create User created
Failure/Error: expect(response).to render_template("new")
expecting <"new"> but was a redirect to <http://test.host/sign_in>
# ./spec/controllers/users_controller_spec.rb:30:in `block (4 levels) in <top (required)>'
How do I resolve this?

According to your controller code, you redirect to sign_in when the save is successful :
format.html { redirect_to sign_in_path, notice: 'User was successfully created.' }
Your failing test is saying :
expecting <"new"> but was a redirect to <http://test.host/sign_in>
It comes from expect(response).to render_template("new"). You might want to change this line to expect(subject).to redirect_to(sign_in_path).
The behaviour render_template("new") might be relevant when #user.save is false

Related

Trouble testing for an error thrown from AASM gem

I'm having trouble testing for a thrown AASM error.
Here is my controller method:
# controllers/jobs_controller.rb
def change_state
respond_to do |format|
if #job.close && #job.save
format.html { redirect_to #job, notice: 'Job has been closed.' }
format.json { render :show, status: :ok, location: #job }
else
format.html { render :show, notice: 'Job could not be closed.' }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
My spec looks like this:
# spec/controllers/jobs_controller_spec.rb
describe "POST #change_state" do
it "cannot transition job from closed" do
job.aasm_state = "closed"
job.save!
post :change_state, params: {id: job.hash_id, user_id: user.id}
expect { response }.to raise_error(AASM::InvalidTransition)
end
end
The test fails (with the expected/desired failure):
Failure/Error: if #job.close && #job.save
AASM::InvalidTransition:Event 'close' cannot transition from 'closed'.
I'm just unable to figure out the right syntax to pass the test. I've tried a few variations of the expect line but can't seem to piece it together.
Any guidance is appreciated.
The exception is happening before the expect statement. Try it:
expect {
post(:change_state, params: { id: job.hash_id, user_id: user.id })
}.to(
raise_error(AASM::InvalidTransition)
)

NoMethodError: undefined method

I am working through Agile Web Development with ruby on rails. While running a test, I get the following:
Error: LineItemsControllerTest#test_should_update_line_item:
NoMethodError: undefined method 'product_id' for nil:NilClass
test/controllers/line_items_controller_test.rb:13:in `block in <class:LineItemsControllerTest>
Here is my test file
require 'test_helper'
class LineItemsControllerTest < ActionController::TestCase
test "should create line_item" do
assert_difference('LineItem.count') do
post :create, product_id: products(:ruby).id
end
assert_redirected_to cart_path(assigns(:line_item).cart)
end
test "should update line_item" do
patch :update, id: #line_item, line_item: { product_id: #line_item.product_id }
assert_redirected_to line_item_path(assigns(:line_item))
end
end
Could someone kindly explain why I get a NoMethodError: undefined method while the book says it should be fine?
Thank you!
Update 1
As per Boltz0r's comment below, here are my create and update methods. I tried comparing what I have versus what is in the book and can't seem to find the problem.
def create
product = Product.find(params[:product_id])
#line_item = #cart.add_product(product.id)
respond_to do |format|
if #line_item.save
format.html { redirect_to #line_item.cart, notice: 'Line item was successfully created.' }
format.json { render :show, status: :created, location: #line_item }
else
format.html { render :new }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #line_item.update(line_item_params)
format.html { redirect_to #line_item, notice: 'Line item was successfully updated.' }
format.json { render :show, status: :ok, location: #line_item }
else
format.html { render :edit }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
I've checked the actual example from the book so will now post the answer so maybe somebody else could see it. If you going to test is update operation you should have something to update first. That's why the test from the book and any other real-life one has setup method to pre-create test item:
setup do
#line_item = line_items(:one)
end
What that error means is that in here:
patch :update, id: (at)line_item, line_item: { product_id: (at)line_item.product_id }
the (at)line_item is nil. so it means you probably have an error either on the create method or maybe in the update method (you are not getting the right line_item)

Not able to call a controller action in Rspec

I have been trying to implement an RSpec for a Controller called "Estimate controller" to test whether my mailing functionality (sending estimate) working properly or not. But I'm not able to call the controller action from my RSpec. I need to set some values (to, subject, message, cc, current_user, attachments) in a hash and send that hash to Estimate controller.Here is what I tried..
estimates_controller_spec.rb
describe "post 'send_estimate'" do
it "should send estimate " do
#estimate = Fabricate(:estimate, id: Faker::Number.number(10), validity: "12/12/2014", total_value: 1222.00, user_id:#user.id, project_id: #project_id)
est_params = {
to: "rspec#rails.com",
subject: "Estimate",
message: "Check out the Estiamte details",
cc: "respec#rails.com",
current_user: #user,
attachments: ""
}
expect{
post :send_estimate, estimate: est_params
}.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end
estimates_controller.rb
def send_estimate
respond_to do |format|
if #estimate.send_email(params[:to], params[:subject], params[:message], params[:cc], current_user, params[:attachments])
#estimate.create_activity :send_estimate, owner: current_user, recipient: #estimate.project
format.html { redirect_to lead_path(#estimate.project), notice: "Email sent Successfully"}
format.json { head :no_content, status: :ok}
else
format.json { render json: #estimate.errors }
format.html { redirect_to contacts_path, notice: 'Something went wrong' }
end
end
end

How would I test a param is getting modified in a RSpec controller test?

I am setting a user through modifying the params instead of creating a hidden_field in the form. As far as I understand, this is a more secure way of handling mass-assignment.
def update
#exercise = Exercise.find(params[:id])
#this is the important part
if params[:exercise][:log_entries_attributes].present?
params[:exercise][:log_entries_attributes].each do |value|
value[1].merge!(:user_id => current_user.id)
end
end
#end the important part
respond_to do |format|
if #exercise.update_attributes(params[:exercise])
format.html { redirect_to_back_or_default #exercise, notice: "Exercise was successfully updated." }
format.mobile { redirect_to #exercise, notice: 'Exercise was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.mobile { redirect_to #exercise, notice: "#{#exercise.errors.full_messages.to_sentence}" }
format.json { render json: #exercise.errors, status: :unprocessable_entity }
end
end
end
In my spec I have the following:
describe "with log_entry_attributes" do
it "updates log_entries_attributes and sets user" do
exercise = FactoryGirl.create(:exercise)
log_entry = FactoryGirl.build(:log_entry)
exercise.log_entries << log_entry
exercise.save
controller.stub(:current_user).and_return(#user)
put :update, :id => exercise.id, :exercise => FactoryGirl.build(:exercise, "log_entries_attributes" => {":0" => {"reps" => "5", "weight" => "5"}}).attributes.symbolize_keys
assigns(:exercise).log_entries.first.user.should eq(#user)
end
end
I get undefined method user for nil:NilClass. I think I know why I get undefined method user. There's just no way to get the association through assigns. I'm not sure how to test that the user_id is being set properly through the current_user. Any help?
Work with mocked object:
exercise = double "exercise"
Exercise.should_receive(:find).and_return(exercise)
and test with:
exercise.should_receive(:update_attributes).with(correct_params)

testing nested route in functional tests

I have a nested route:
resources :stories do
resources :comments
end
this is my create method in controller:
def create
#story = Story.find(params[:story_id])
#comment = #story.comments.build(params[:comment])
#comments = #story.comments.all
respond_to do |format|
if #comment.save
format.html { redirect_to #story, notice: t('controllers.comments.create.flash.success') }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render template: "stories/show" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
And here is it's test:
setup do
#comment = comments(:one)
#story = stories(:one)
end
....
test "should create comment" do
assert_difference('Comment.count') do
post :create, :story_id => #story.id, comment: { content: #comment.content, name: #comment.name, email: #comment.email }
end
assert_redirected_to #story_path
end
that ends up with this error:
1) Failure:
test_should_create_comment(CommentsControllerTest) [/home/arach/workspace/Web/ruby/nerdnews/test/functional/comments_controller_test.rb:25]:
Expected response to be a redirect to <http://test.host/stories/980190962/comments> but was a redirect to <http://test.host/stories/980190962>
I don't know why the test expect to redirect to stories/:id/comments. I tried other things like story_comment_path but it didn't help either. story_path without # also ends up with another error:
ActionController::RoutingError: No route matches {:action=>"show", :controller=>"stories"}
same error happens for story_path, :story_id => #story.id. Any idea why this happens?
I think it should be story_path(#story.id). See here.

Resources