rspec testing has_many :through and after_save
I've had similar problems in the past that have been resolved by reloading the association (rather than the parent object).
Does it work if you reload thing.followers
in the RSpec?
it "should have followers" do
@thing.followers.reload
@thing.followers.should == [@user]
end
EDIT
If (as you mention) you're having problems with the callbacks not getting fired then you could do this reloading in the object itself:
class Thing < ActiveRecord::Base
after_save { followers.reload}
after_save :do_stuff
...
end
or
class Thing < ActiveRecord::Base
...
def do_stuff
followers.reload
...
end
end
I don't know why RSpec has issues with not reloading associations but I've hit the same types of problems myself
Edit 2
Although @dantswain confirmed that the followers.reload
helped alleviate some of the problems it still didn't fix all of them.
To do that, the solution needed a fix from @kikuchiyo which required calling save
after doing the callbacks in Thing
:
describe Thing do
before :each do
...
@user.things << @thing
@thing.run_callbacks(:save)
end
...
end
Final suggestion
I believe this is happening because of the use of <<
on a has_many_through
operation. I don't see that the <<
should in fact trigger your after_save
event at all:
Your current code is this:
describe Thing do
before(:each) do
@user = User.create!(:name => "Fred")
@thing = Thing.create!(:name => "Foo")
@user.things << @thing
end
end
class Thing < ActiveRecord::Base
after_save :do_stuff
...
def do_stuff
followers.each { |f| puts "I'm followed by #{f.name}" }
end
end
and the problem is that the do_stuff
is not getting called. I think this is the correct behaviour though.
Let's go through the RSpec:
describe Thing do
before(:each) do
@user = User.create!(:name => "Fred")
# user is created and saved
@thing = Thing.create!(:name => "Foo")
# thing is created and saved
@user.things << @thing
# user_thing_relationship is created and saved
# no call is made to @user.save since nothing is updated on the user
end
end
The problem is that the third step does not actually require the thing
object to be resaved - its simply creating an entry in the join table.
If you'd like to make sure that the @user does call save you could probably get the effect you want like this:
describe Thing do
before(:each) do
@thing = Thing.create!(:name => "Foo")
# thing is created and saved
@user = User.create!(:name => "Fred")
# user is created BUT NOT SAVED
@user.things << @thing
# user_thing_relationship is created and saved
# @user.save is also called as part of the addition
end
end
You may also find that the after_save
callback is in fact on the wrong object and that you'd prefer to have it on the relationship object instead. Finally, if the callback really does belong on the user and you do need it to fire after creating the relationship you could use touch
to update the user when a new relationship is created.
Table count does not increase after save returns true for nested resource rspec test
As I was using Mongoid and 'Mastertags' was embedded into Project, there is no separate collection for Mastertags.
I had to change the code to :
describe "POST create" do
context "with valid params" do
it "creates a new Mastertag" do
expect {
post :create, { project_id: @project.id, mastertag: FactoryGirl.attributes_for(:mastertag_without_project) }
}.to change {@project.reload.mastertags.count}.by(1)
end
end
I got help from this Stackoverflow question : RSpec/Mongoid: Expect to change count on embedded models
Testing before_save with RSpec and Factory Girl
It looks like your record is just showing you old data, and needs to be refreshed. Try:
record.reload.item_id.should == 2
Testing models with relationships and callbacks in Rails with RSpec and Factory_Girl
update_attribute
is for updating the attributes of the current object. That means you need to call update_attribute
on the object whose attribute you want to update. In this case, you want to update the recipe, not the ingredient. So you have to call recipe.update_attribute(:total_percentage, ...)
.
Also, ingredients belong to recipes, not other ingredients. So instead of self.ingredients.sum(:percentage)
you should really be calling recipe.ingredients.sum(:percentage)
.
Also, you'll need to reload @recipe
before testing it's total_percentage
. Even though it refers to the same database record as @ingredient.recipe
, it's not pointing to the same Ruby object in memory, so updates to one won't appear in the other. Reload @recipe
to fetch the latest values from the database after saving the @ingredient
.
has_many :through creating child after_save -- ActionView::Template::Error
Besides being very weird to create a random value in the after_save
callback (which I think you're doing as an exercise, but anyway it's better to use good practices from the start), you should never use rand(Model.count)
to get a sample record. There's two main problems:
- The
rand(upper_bound)
method returns a number between zero and theupper_bound
argument, but there's no guarantee that zero is the first created id. I'm using PostgreSQL and the first model has the id 1. You can specify a range (rand(1..upper_bound)
), but anyway you're gambling on the way the current database works. - You're assuming that all the records exist in a sequential order at any given time, which is not always true. If you delete a record and it's id is randomly chosen, you'll get an error. The library also can use any strategy to create the fixtures, so it's better not to assume anything about how it works.
If you really need to choose randomly a record, I'd recommend simply using the array's sample
method: Food.all.sample
. It's slow, but it works. If you need to optimize, there's other options.
Now, I'd really recommend to avoid random values at all costs, using them only when necessary. It's difficult to test, and difficult to track bugs. Also, I'd avoid creating a relation inside a callback, it grows rapidly into a unmanageable mess.
Rspec controller test for callback after_save
I am assuming that the guest_params
method in the controller looks something like this:
def guest_params
params.require(:guest).permit(....)
end
If that is the case, you need to update the POST call in your test case thusly:
post :create, {guest: params}
On a side note, your controller is unnecessarily bloated. I would read up on working with associated models to streamline your code, specifically, using accepts_nested_attributes_for
:
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
RSPEC test NAME ERROR - Undefined Local variable or method
You're absolutely right - because you defined the associated post method inside of post_spec.rb, it can't be called from inside vote_spec.rb.
You have a couple options: you can copy your associated post method and put it inside vote_spec.rb, or you can create a spec helper file where you define associated_post once and include it in both vote_spec.rb and post_spec.rb. Hope that helps!
Related Topics
Trying to Understand Use of Self.Method_Name VS. Classname.Method_Name in Ruby
Connect to Tor Network with Ruby
How to Create Temp Dir in Ruby
Is the Unix Philosophy Falling Out of Favor in the Ruby Community
Create Hash from Array and Frequency
Ruby on Rails Config.Secret_Token Error
Is Assignment in a Conditional Clause Good Ruby Style
Does Multibyte Character Interfere with End-Line Character Within a Regex
Require Lib in Rspec with Ruby 1.9.2 Brings "No Such File to Load"
Sequel Accessing Many_To_Many Join Table When Adding Association
Best Way to Handle Data Attributes in Slim
Mechanize How to Get Current Url
Regex to Match Hashtags in a Sentence Using Ruby
Passenger: Cannot Load Such File Rubygems/Builder
"Gem Update --System Is Disabled on Debian" Error
(Ruby) How to Check Whether a Range Contains a Subset of Another Range
Iterate an Array, N Items at a Time
Differences Between Literals and Constructors? ([] VS Array.New and {} VS Hash.New)