Some of my tests don't work because of saved data in my database. I found out (code below) that the create function of FactoryGirl doesn't go through the controller steps to ensure that everything defined in the controller is passed. One thing that my controller does is format an attribute before saving to database to ensure that a string is saved and not an array, e.g. "Monday,Tuesday" instead of ["Monday,Tuesday"].
if Schedule.count == 0
FactoryGirl.create(:schedule)
end
How do I set the before statement to make the create functionality execute controller steps?
Solved this by putting the following line:
before { FactoryGirl.create(:default_for_overlap_schedule) }
Full test is:
describe "has schedule overlap" do
before { FactoryGirl.create(:default_for_overlap_schedule) }
let!(:overlap_schedule) { FactoryGirl.attributes_for(:overlap_schedule) }
it "prompts user of error" do
expect{ post :create, schedule: overlap_schedule }.not_to change(Schedule, :count).by(1)
flash[:error].should eq("Schedule has an overlap")
end
end
Related
I'm starting to attempt to incorporate more testing into my code, but I've hit a wall.
My model looks something like this
class Image < ActiveRecord:Base
before_create :do_something_general
before_update :do_something_on_update, :do_something_general
belongs_to :captureable, polymorphic: true
mount_uploader :image, SomeUploader
...
end
My rspec looks something like
describe SomeModel do
before :each do
#image = FactoryGirl.create(:image)
end
...
describe "moving image" do
context "change the parent of the image" do
it "moves" do
new_parent = FactoryGirl.create(:parent)
current_file_path = #image.image.file.path
#image.captureable = new_parent
#image.save!
#image.image.file.path.should_not == current_file_path
end
end
end
end
When I first create an Image, it will get stored in a file tree structure that depends on its parents. When a parent changes, the Image should be moved, and this is done with the before_update callback :do_something_on_update. My test should verify that when the Image has had its parent changed, it is located in a new location.
The problem is, when #image.save.should be_valid an except is returned because :do_something_general is run before :do_something_on_update (the order is important). It seems that the rspec thinks I'm creating a new object (using debugger I've checked that the object id doesn't change when modifying it), and thus runs before_create instead of before_update.
Edit: it seems that before_update is working, but only on callback methods that are in the class, but not in the module. In this case, :do_something_on_update is located in an included module.
End Edit
When I try this in the console in development mode, it works as expected.
Other things to note: I'm using Carrierwave for uploading (the image column is a carrierwave uploader) and when the :image factory is called, it also creates several parents and grandparent objects. Using Rspec 2.10, Rails 3.2.8, Ruby 1.9.3
Looking forward to your responses.
Thanks.
I would expect image.save.should be_valid to fail, because it's going to invoke image.save, which returns true or false, then it's going to invoke #valid? on that boolean result, which should likely fail.
You might consider writing your test like so:
describe SomeModel do
let(:image) { FactoryGirl.create(:image) }
context "when changing the parent of the image" do
let(:parent_change) { lambda {
image.captureable = FactoryGirl.create(:parent)
image.save!
} }
it "updates the image's path" do
expect parent_change.to change { image.image.file.path }
end
end
end
This ensures that you only have one assertion in the test (that the file path is changing), and that if the save fails, it will instead raise an exception.
I'm writing the following test:
let!(:city_areas) { FactoryGirl.create_list(:city_area, 30) }
before {
#city_areas = mock_model(CityArea)
CityArea.should_receive(:where).and_return(city_areas)
}
it 'should assign the proper value to city areas variable' do
get :get_edit_and_update_vars
assigns(:city_areas).should eq(city_areas.order("name ASC"))
end
to test the following method:
def get_edit_and_update_vars
#city_areas = CityArea.where("city_id = '#{#bar.city_id}'").order("name ASC").all
end
However, it fails out, saying that there's no method 'city_id' for nil:NilClass, leading me to believe it's still attempting to use the instance variable #bar.
How do I properly stub out this where statement to prevent this?
Why are you doing #city_areas = mock_model(CityArea) and then you never use #city_areas again?
I would test it this way:
inside the model CityArea create a named scope for this: where("city_id = '#{#bar.city_id}'").order("name ASC")
then in your controller spec you do
describe 'GET get_edit_and_update_vars' do
before(:each) do
#areas = mock('areas')
end
it 'gets the areas' do
CityArea.should_receive(:your_scope).once.and_return(#areas)
get :get_edit_and_update_vars
end
it 'assign the proper value to city areas variable' do
CityArea.stub!(:your_scope => #areas)
get :get_edit_and_update_vars
assigns(:city_areas).should eq(ordered)
end
end
and you should also create a spec for that new scope on the model spec
just a tip, you shouldn't use should_receive(...) inside the before block, use stub! inside before and use should_receive when you want to test that method is called
also, you shouldn't need to use factorygirl when testing controllers, you should always mock the models, the model can be tested on the model spec
I have 2 models like the following
Class Post
has_many :comments, :dependent => :destroy
end
Class Comment
validates_presence_of :post
validates_presence_of :comment
belongs_to :post
end
In Comments controller,
def create
comment = #post.comments.build(params[:comment])
if comment.save
// some code
else
// some code
end
end
When the comment is invalid as per the validation, the comment is not saved. But when the #post object is accessed in the view, it contains a comment object with nil id. This did not happen in Rails 2.3.11. We are upgraded to Rails 3.1 and then now to Rails 3.2. This comment object with nil id disappears when I do #post.reload. We are using REE.
I tried to interchange build and new methods. It had the same result as build. Similar behavior is found across our application. Is it the expected behavior or am I doing something wrong?
This seems like expected behaviour to me.
via http://guides.rubyonrails.org/association_basics.html#belongs_to-association-reference
4.1.1.3 build_association(attributes = {})
The build_association method returns a new object of the associated
type. This object will be instantiated from the passed attributes, and
the link through this object’s foreign key will be set, but the
associated object will not yet be saved.
When you call #post.comments.build(...), Rails:
Creates a new Comment object
sets comment.post_id to #post.id.
Inserts it into the comments array (in memory).
When the validation fails, it doesn't delete the comment and the comment persists in the in-memory comments array. When #post gets to your view, #post.comments still includes that badly validated comment.
As for how to deal with it, I'm not sure. Maybe you could do something like (in your controller)... (Feels pretty ugly though.)
def create
comment = #post.comments.build(params[:comment])
if comment.save
// some code
else
#bad_comment = #post.comments.pop
end
end
I had a similar problem while using rails 3.2
Firstly, you need to create two separate methods in your controller. They will be as follows:
The 'new' method that is used to build your comments using 'build_association'
def new
#post = Post.new
comment = #post.build_comments
end
The 'create' method to actually create your comments using 'create_association'
def create
#post = Post.new(params[:post])
comment = #post.create_comments(params[:post][:comment_attributes])
if comment.save
// some code
else
#bad_comment = #post.comments.pop
end
end
Note: I suggest passing 'comment' attribute as a nested attribute of 'post' through your form using 'fields_for'.
Please refer:
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I am trying to add a file to a model using qqfile (though that really isn't relevant here).
I look at the params being passed to the server for update, and I have
{ id: 63, photo: 'foto_file.jpg'}
My understanding was that if an object was passed with an id parameter, rails would understand that as an already existing object, and update that model. If no id parameter is present, Rails would use create.
Is that not correct?? How in this instance can I tell rails to update rather than create?
I'm assuming more code isn't needed here, as my controllers won't really help with the solution because I think the decision is made by rails before it really hits the controller. But I'm happy to post the controller code if it is needed.
--------------- my javascript used to update or create the model ---------------------
render: function(){
var start_form=HandlebarsTemplates['user/userForm'](user.attributes);
$(this.el).html(start_form);
var uploader = new qq.FileUploader({
element: document.getElementById('file-upload'),
action: '/users',
onSubmit: function(id, fileName){
if(MyApp.user.id){
uploader.setParams({
id: MyApp.user.id
});
}
},
debug: true
});
},
The update method is only used when you sent a PUT request, not a POST request. Make sure you're using the PUT method. (If you show your form's code, I can give a more specific answer).
Update -- With your code, try adding this as a parameter to your qq.FileUploader call:
params: {
_method: "put"
}
Rails will look for a _method parameter to handle PUT/DELETE requests.
I couldn't get Dylan's javascript method to work, so in my controller I redirected to my update if the response had an id.
def create
if params[:id]
return self.update
end
#then all my regular create stuff here
end
def update
#all the usual update stuff
end
All I'm trying to do is spec how a one line helper method for a view should behave, but I'm not sure what kind of mock object, (if any) I should be creating if I'm working in Rails.
Here's the code for events_helper.rb:
module EventsHelper
def filter_check_button_path
params[:filter].blank? ? '/images/buttons/bt_search_for_events.gif' : '/images/buttons/bt_refine_this_search.gif'
end
end
And here's my spec code, in events_helper_spec.rb:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
#Delete this example and add some real ones or delete this file
it "should be included in the object returned by #helper" do
included_modules = (class << helper; self; end).send :included_modules
included_modules.should include(EventsHelper)
end
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {}
params[:filter] = true
# create an instance of the class that should include EventsHelper by default, as the first test has verified (I think)
#event = Event.new
# call method to check output
#event.filter_check_button_path.should be('/images/buttons/bt_search_for_events.gif')
end
end
When I've looked through the docs here - http://rspec.info/rails/writing/views.html, I'm mystified as to where the 'template' object comes from.
I've also tried looking here, which I thought would point me in the right direction, but alas, no dice. http://jakescruggs.blogspot.com/2007/03/mockingstubbing-partials-and-helper.html
What am I doing wrong here?
Thanks,
Chris
You are not doing anything in that spec, just setting a stub, so it will pass, but hasn't tested anything.
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {:filter => true}
helper.stub!(:params).and_return(params)
helper.filter_check_button_path.should eql('/images/buttons/bt_search_for_events.gif')
end
end
I'm running my test without spec_helper (Ruby 1.9)
require_relative '../../app/helpers/users_helper'
describe 'UsersHelper' do
include UsersHelper
...
end
Ah,
I asked this question on the rspec mailing list, and one kind soul (thanks Scott!) explained to me that there's a handy helper object for this, that you should use instead, like so:
Rails has its own helper function
params = {:filter => true}
helper.stub!(:params).and_return(params)
I've now updated the code like so:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
#Delete this example and add some real ones or delete this file
it "should be included in the object returned by #helper" do
included_modules = (class << helper; self; end).send :included_modules
included_modules.should include(EventsHelper)
end
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {}
params[:filter] = true
helper.stub!(:filter_check_button_path).and_return('/images/buttons/bt_search_for_events.gif')
end
end
And it's working. Huzzah!