Rspec, testing for update controller not working? - ruby-on-rails-3

This is a homework I am working towards to familiar myself with TDD and Rspec. But somehow
I don't understand why the following test failed:
describe 'update' do
fixtures :movies
before :each do
#fake_movie = movies(:star_wars_movie)
end
it 'should retrieve the right movie from Movie model to update' do
Movie.should_receive(:find).with(#fake_movie.id.to_s).and_return(#fake_movie)
put :update, :id => #fake_movie.id, :movie => {:rating => #fake_movie.rating}
end
it 'should prepare the movie object available for update' do
put :update, :id => #fake_movie.id, :movie => {:rating => #fake_movie.rating}
assigns(:movie).should == #fake_movie
end
it 'should pass movie object the new attribute value to updated' do
fake_new_rating = 'PG-15'
#fake_movie.stub(:update_attributes!).with("rating" => fake_new_rating).and_return(:true)
put :update, :id => #fake_movie.id, :movie => {:rating => fake_new_rating}
#fake_movie.should_receive(:update_attributes!).with("rating" => fake_new_rating).and_return(:true)
end
end
The error message I got is:
Failures:
1) MoviesController update should pass movie object the new attribute value to updated
Failure/Error: #fake_movie.should_receive(:update_attributes!).with("rating" => fake_new_rating).and_return(:true)
(#<Movie:0xd39ea38>).update_attributes!({"rating"=>"PG-15"})
expected: 1 time
received: 0 times
# ./spec/controllers/movies_controller_spec.rb:99:in `block (3 levels) in <top (required)>'
Finished in 0.60219 seconds
12 examples, 1 failure
Failed examples:
rspec ./spec/controllers/movies_controller_spec.rb:95 # MoviesController update should pass movie object the new attribute value to updated
Basically it says that my last line of test failed #fake_movie.should_receive(:update_attributes!).with("rating" => fake_new_rating).and_return(:true), and I think it didn't receive the function call 'update_attributes!` at all, but why?
And the controller code:
def update
#movie = Movie.find params[:id]
#movie.update_attributes!(params[:movie])
flash[:notice] = "#{#movie.title} was successfully updated."
redirect_to movie_path(#movie)
end
Thanks in advance

Should be:
it 'should pass movie object the new attribute value to updated' do
fake_new_rating = 'PG-15'
Movie.stub(:find).and_return(#fake_movie)
#fake_movie.should_receive(:update_attributes!).with("rating" => fake_new_rating).and_return(:true)
put :update, :id => #fake_movie.id, :movie => {:rating => fake_new_rating}
end
Otherwise, the line #movie = Movie.find params[:id] would query against the model.

Related

Issue updating file attachment(image) using Attachment_fu

I am able to upload image files as attachments using Attachment_fu but when I try to edit/ modify images which were already uploaded, it does not work.
In my Controller I have this:
def update
#sponsor_account = SponsorAccount.find( params[:id] )
if params[:showcase_category_image_file] &&
!params[:showcase_category_image_file].blank?
#sponsor_account.showcase_category_image =
ShowcaseCategoryImage.new(:uploaded_data =>
params[:showcase_category_image_file])
***This logs - Now the file name is: ***
Rails.logger.info("Now the file name is: #
{#sponsor_account.showcase_category_image.filename}")
end
#sponsor_account.update_attributes( params[:sponsor_account] )
if #sponsor_account.save
flash[:notice] = "Showcase category #{#sponsor_account.name} was updated!"
else
flash[:errors] = #sponsor_account.errors
end
redirect_to sponsor_accounts_path
end
ShowcaseCategoryImage is defined as follows:
has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 5.megabytes,
:thumbnails => { :large => [350, 100], :medium => [200, 90], :thumb =>
[35,35], :preview => [60,60] }
validates_as_attachment
The view has a file_field_tag as follows:
<%= file_field_tag :showcase_category_image_file%>
and my SponsorAccount model says:
has_one :showcase_category_image, :as => :owner, :dependent => :destroy
validates_presence_of :showcase_category_image, :on => :create
Almost similar code works perfectly ok in 'create' but here in 'update' action where there is already a value, this code is not working.
I am getting the below error msgs:
Completed 500 Internal Server Error in 1089ms
ActionView::Template::Error (undefined method `public_filename' for nil:NilClass):
Obviously this error is in the index action where it tries to list all records and their attachments. Since this attachment is empty after the update, this error is thrown in the redirect_to part.
I am using REE1.8.7 and rails 3.2.9
Please help!
This issue was solved when I added :multipart => true in the 'View'. I am using rails 3.2.9 and the rails api has this to say about the 'file_field_tag':
file_field_tag(name, options = {}) Link
Creates a file upload field. If you are using file uploads then you will also need to set the multipart option for the form tag:

how to pass id of the controller

How to pass id. my controller is:
class AttachementsController < ApplicationController
def index
#pdf = Attachement.find(params[:resume_id])
# send_file(#pdf.file.url, :type => 'application/pdf', :disposition => 'inline',:stream => false)
redirect_to #pdf.file.url
end
end
and my test case of the controller is:
require 'spec_helper'
describe AttachementsController do
describe "GET 'index'" do
it "should be successful" do
get 'index', :id => "#attachements.id"
response.should be_success
end
end
end
and my error is:
AttachementsController GET 'index' should be successful
Failure/Error: get 'index', :id => "#attachements.id"
ActiveRecord::RecordNotFound:
Couldn't find Attachement without an ID
# ./app/controllers/attachements_controller.rb:3:in `index'
# ./spec/controllers/attachements_controller_spec.rb:7:in `block (3 levels) in <top (required)>'
You don't need the quotes around #attachements.id.
get 'index', :id => #attachements.id

Rspec what is wrong with my test for create controller action?

I am trying to learn TDD and this is part of my homework I couldn't figure out how to do it.
I want to test create controller action, and here is my code for test:
require 'spec_helper'
describe MoviesController do
describe 'create' do
it 'should call the model method perform create!' do
Movie.should_receive(:create!).with({"title" => 'Milk', "rating" => 'R'})
post :create, :movie => {:title => 'Milk', :rating => 'R'}
end
end
end
But I got:
Failures:
1) MoviesController create should call the model method performe create!
Failure/Error: post :create, :movie => {:title => 'Milk', :rating => 'R'}
NoMethodError:
undefined method `title' for nil:NilClass
# ./app/controllers/movies_controller.rb:50:in `create'
# ./spec/controllers/movies_controller_spec.rb:7:in `block (3 levels) in <top (required)>'
Finished in 0.21714 seconds
And here is create action I am testing against. Yes, it is TDD, and yes, I am testing a
working code, and it is the testing doesn't working :D
def create
#movie = Movie.create!(params[:movie])
flash[:notice] = "#{#movie.title} was successfully created."
redirect_to movies_path
end
I don't even know why I got the undefined method error message? I have a few other test passed but I deleted in this code snippe for simplicity, so I don't think it is related db/model related config problem. But why it does not working and how to change it?
Cheers
When you do a should_receive like that, it will return nil so the next line (when setting the flash message) attempts to retrieve the title from nil. Change your should_receive to:
Movie.should_receive(:create!).with({"title" => 'Milk', "rating" => 'R'}).and_return(stub_model(Movie))

Changing boolean value in Rails 3 app yields ArgumentError

In my Rails 3.0.5 app I have columns on my Profile model for privacy. In each User's settings, I want the user to be able to change their settings so they can determine which parts of their profile are shown. In my migration I added four boolean attributes for this. However when I try to update the value (switch between true/false) I get an ArgumentError:
ArgumentError (wrong number of arguments (1 for 2)):
app/controllers/profiles_controller.rb:162:in `edit_settings'
An example of the params passed:
{"utf8"=>"✓",
"authenticity_token"=>"0uySkgRNsIIQSX6PtXl3e0e+MXCTo4yuoU/QjuBxENw=",
"show_hometown"=>"1"}
Here is the boolean in my migration:
t.boolean :show_hometown, :default => true
Here is the controller action edit_settings I'm using to update. There are four if statements to update_attribute, but for length I'm only including two:
def edit_settings
#profile = current_user.profile
if #profile.update_attribute(:show_hometown)
redirect_to settings_path, :notice => 'Updated user information successfully.'
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
...
if #profile.update_attribute(:show_current_location)
redirect_to settings_path, :notice => 'Updated user information successfully.'
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
end
And the form for updating :show_hometown:
<%= form_tag({:action => "edit_settings", :controller => "profiles"}, :html => {:multipart => true }) do %>
<%= check_box_tag :show_hometown %>
<%= #user.profile.hometown %>
<% end %>
FOLLOW-UP:
If I use validations on :show_hometown and insert the following into my controller I can see the value toggle:
def edit_settings
#profile = current_user.profile
if #profile.show_hometown == true
if #profile.update_attributes(:show_hometown => false)
redirect_to settings_path
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
elsif #profile.show_hometown == false
if #profile.update_attributes(:show_hometown => true)
redirect_to settings_path
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
end
end
However, since I have four booleans where I'll apply the same code is there a way to handle each update in one controller action? Or do I need to create a separate action for each boolean attribute I want to update?
You have to tell update_attribute the column and the value:
#profile.update_attribute(:show_hometown, params[:show_hometown])
This means set the value of column show_hometown to the value in params[:show_hometown].
Note that with update_attribute validations are skipped. If you want validations to run, you have to use update_attributes:
#profile.update_attributes(:show_hometown => params[:show_hometown])
To answer your follow-up: with update_attributes you can update several columns in one call:
#profile.update_attributes({:show_hometown => !#profile.show_hometown,
:show_birthdate => !#profile.show_birthdate})
There is also a toggle method, but using that would result in 4 database calls instead of 1. It can be written in one line:
%w{show_hometown show_birthdate}.each { |attr| #profile.toggle(attr) }

Ruby Problemewith filter and RSpec

I can't figure out why this RSpec test fails. Any advice?
I try to handle the destruction of post. Only users who create posts can delete them.
class PostsController < ApplicationController
before_filter :authorized_user, :only => :destroy
def destroy
post = Post.find(params[:id])
post.destroy
redirect_to posts_path, notice: 'Post successfully destroyed'
end
private
def authorized_user
redirect_to posts_path, notice: 'Access Denied' if current_user.posts.find_by_id(params[:id]).nil?
end
Test :
describe "DELETE destroy" do
before(:each) do
#post = stub_model(Post, :id => 23)
#post.stub(:destroy){true}
Post.stub(:find){#post}
end
it "should search the post" do
Post.should_receive(:find).with(#post.id.to_s).and_return(#post)
delete :destroy, {:id => #post.id }
end
it "should destroy the post" do
#post.should_receive(:destroy)
delete :destroy, {:id => #post.id }
end
it "should redirect to the posts list" do
delete :destroy, {:id => #post.id }
response.should redirect_to posts_path
end
end
And errors :
1) PostsController DELETE destroy should search the post
Failure/Error: Post.should_receive(:find).with(#post.id.to_s).and_return(#post)
(<Post(id: integer, title: string, body: text, created_at: datetime, updated_at: datetime, user_id: integer) (class)>).find("23")
expected: 1 time
received: 0 times
# ./spec/controllers/posts_controller_spec.rb:67:in `block (3 levels) in <top (required)>'
I think it is because in the before(:each) block you have to log in a user. You have before_filter and this filter is only for :destroy. So when you don't have a logged in user then your tests will fail because it is actually a user without permissions that "runs the test". So you should put this code in before(:each):
user = way_to_make_user - I do it with: FactoryGirl.create(:user,role=>"client")
controller.sign_in user
I could help you more if you tell me what do you use for authentication. I use cancan gem. So the main point is that you have to log in user before each test.