Testing model that acts_as_nested_set with RSpec - ruby-on-rails

I have an Organization model that acts_as_nested_set, using awesome_nested_set:
class Organization < ActiveRecord::Base
acts_as_nested_set
attr_accessible :name, :location_id, :parent_id
has_many :org_prod_relationships, dependent: :destroy
has_many :products, through: :org_prod_relationships
def has_product?(prod)
org_prod_relationships.find_by_product_id(prod.id)
end
def add_product!(prod)
org_prod_relationships.create!(product_id: prod.id)
end
def publish_product!(prod)
self.descendants.each do |d|
d.add_product!(prod)
end
end
end
How can I write a test in RSpec for publish_product!, and/or is this the wrong approach to creating org_product_relationships in a nested set, and therefore hard to test? My non-working attempt is here (clipped from the larger spec file) https://gist.github.com/3911555.
EDIT: Updating to include the error message. Note, lines 79 and 80 are:
it { should have_product(product) }
its(:products) { should include(product) }
in the gist.
Failures:
1) Organization publishes_product
Failure/Error: it { should have_product(product) }
expected #has_product?(#<Product id: 34, name: "floo powder", created_at: "2012-10-21 14:15:08", updated_at: "2012-10-21 14:15:08", photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil>) to return true, got false
# ./spec/models/organization_spec.rb:79:in `block (3 levels) in <top (required)>'
2) Organization publishes_product products
Failure/Error: its(:products) { should include(product) }
expected [] to include #<Product id: 35, name: "floo powder", created_at: "2012-10-21 14:15:08", updated_at: "2012-10-21 14:15:08", photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil>
Diff:
## -1,2 +1,2 ##
-[#<Product id: 35, name: "floo powder", created_at: "2012-10-21 14:15:08", updated_at: "2012-10-21 14:15:08", photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil>]
+[]
# ./spec/models/organization_spec.rb:80:in `block (3 levels) in <top (required)>'
Finished in 29.93 seconds
184 examples, 2 failures
Failed examples:
rspec ./spec/models/organization_spec.rb:79 # Organization publishes_product
rspec ./spec/models/organization_spec.rb:80 # Organization publishes_product products

I think you have to reload an object. Try the following:
#organization.products.reload
After you publish products in the before statements. Basically once the #organization is saved, calling #organization.products has a force_reload=false. Reference: http://guides.rubyonrails.org/association_basics.html#has_many-association-reference
Also since you asked, given that you have set up the relationships, you actually should be able to define your methods using the products relationship:
def has_product?(prod)
products.include? prod
end
def add_product!(prod)
products << prod
end
I think if you define the methods that way, you may not have to reload because the organization.products association is already updated.
Hope that helps.

Related

Rails 5.2 belongs_to association not linking to parent

I have these models
class Version
has_many :bids
end
class Bid
belongs_to :version
end
in the console:
> bid = Bid.first
which returns
> #<Bid id: 8 version_id: 5, deleted_at: nil, created_at: "2018-09-06 00:32:32", updated_at: "2018-09-06 00:32:32", created_by_id: 3, updated_by_id: 3, selected: true>
but if i try to call the version it returns nil. the version is there though
> bid.version
> nil
> Version.find(bid.version_id)
> #<Version id: 5, effective_date: "2018-09-05 23:36:24", end_date: nil, created_at: "2018-09-05 23:36:24", updated_at: "2018-09-05 23:36:24", created_by_id: nil>
what is going on? what could I be doing wrong?
So it turns out that object.version is an existing rails method. if i change the relationship to belongs_to :pricing_version the code works.
Anyone with this issue in the future just avoid naming their class Version

Testing Rails views in RSpec: why does it route to "show" when I want to test "index"?

I have this very basic view spec in spec/views/users/index_spec.rb:
require 'rails_helper'
RSpec.describe "users/index", type: :view do
before(:each) do
#user = create(:user)
assign(:users, [#user])
end
it "renders a list of users" do
render
expect(page).to have_selector "tr##{dom_id(#user)}"
end
end
When executing it, it's telling me the following:
Failures:
1) users/index renders a list of users
Failure/Error: render
ActionView::Template::Error:
No route matches {:action=>"show", :controller=>"users", :id=>nil, :locale=>#<User id: 1, name: "Rosalinda Dach", email: "marilie#leffler.ca", encrypted_password: "$2a$04$G/z6lbFUpnh9FD3bymYBE.LrJK3acKr4TsURgCq7B77...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, confirmation_token: nil, confirmed_at: "2015-06-03 14:33:11", confirmation_sent_at: nil, unconfirmed_email: nil, failed_attempts: 0, unlock_token: nil, locked_at: nil, created_at: "2015-06-03 14:33:12", updated_at: "2015-06-03 14:33:12", avatar: nil>} missing required keys: [:id]
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/journey/formatter.rb:46:in `generate'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:727:in `generate'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:758:in `generate'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:801:in `url_for'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:280:in `call'
# /Users/josh/.rvm/gems/ruby-2.1.0#a4aa2/gems/actionpack-4.2.1/lib/action_dispatch/routing/route_set.rb:345:in `block (2 levels) in define_url_helper'
Why does the test route to users#show instead of users#index?
You can change your code:
RSpec.describe "users/index.html.erb", type: :view do
Since I didn't find a suitable solution, I decided to create my own online Markdown editor with copy and paste: PIMP (Pasteable Images, Markdown, Pandoc).
http://pimp.suhail.uberspace.de/en
It's not yet looking very great yet, but it offers everything you need to create semantically awesome documents using Pandoc's very powerful Markdown, paste images directly from clipboard into the text, and export the documents to various formats (at the time being Docx, Epub, Odt, maybe later also PDF).
At the time being, to use PIMP, one has to create a user account.
The whole project is rather a proof of concept than a real project, but if people like and use it, I would be happy to make it a real project.

FactoryGirl creating extra records with has_many association

I'm running into an issue where FactoryGirl appears to be creating extra records with a has_many relationship.
Given these models:
class NextAction < ActiveRecord::Base
has_many :next_actions_orders
has_many :orders, through: :next_actions_orders
end
class NextActionsOrder < ActiveRecord::Base
belongs_to :order
belongs_to :next_action
end
class Order < ActiveRecord::Base
has_many :next_actions_orders
has_many :next_actions, through: :next_actions_orders
end
And these factories:
FactoryGirl.define do
factory :next_action do
status :pending
trait :pickup do
next_actions_orders { FactoryGirl.create_list(:next_actions_order, 1) }
action_type :pickup
end
trait :multiple_pickups do
next_actions_orders { FactoryGirl.create_pair(:next_actions_order) }
action_type :pickup
end
end
end
FactoryGirl.define do
factory :next_actions_order do
order { FactoryGirl.create(:order) }
next_action
end
end
FactoryGirl.define do
factory :order do
status :pending
end
end
As you can see in the NextAction factory, I ran into an issue setting up the NextActionOrder association.
I would usually have used next_actions_orders { FactoryGirl.create(:next_actions_order) } but with the has_many :next_actions_orders, I was getting an undefined method 'each' for #<NextActionsOrder... error.
next_actions_orders { FactoryGirl.create_list(:next_actions_order, 1) } seems to work as a workaround. As shown below, this doesn't seem to be the cause of the issue, since it also exists from the create_pair example.
The real issue is this:
it 'create_list generates duplicate FactoryGirl records' do
puts NextAction.count # output: 0
pickup = create(:next_action, :pickup)
puts NextAction.count # output: 2
## binding.pry
end
Succinctly, the create(:next_action) calls seem to be generating 1 additional NextAction record than is required.
I used pry to inspect this and sure enough, you can see this.
With pry inserted at the first location shown above, queries produce the following:
pry(#<RSpec::Core::ExampleGroup::Nested_1>)> NextAction.all
=> [#<NextAction id: 2, ... created_at: "2014-04-20 16:40:57", updated_at: "2014-04-20 16:40:57", status: 0>,
#<NextAction id: 3, ... created_at: "2014-04-20 16:40:57", updated_at: "2014-04-20 16:40:57", status: 0>]
pry(#<RSpec::Core::ExampleGroup::Nested_1>)> NextActionsOrder.all
=> [#<NextActionsOrder id: 1, next_action_id: 3, order_id: 2>]
pry(#<RSpec::Core::ExampleGroup::Nested_1>)> Order.all
=> [#<Order id: 2, created_at: "2014-04-20 16:40:57", updated_at: "2014-04-20 16:40:57", status: 0>]
Here, everything looks great, except for NextAction ID #2. It's not associated anywhere, it's just an orphan record that was created for some reason.
Here's what happens with create_pair:
it 'create_pair generates duplicate FactoryGirl records' do
puts NextAction.count # output: 0
pickups = create(:next_action, :multiple_pickups)
puts NextAction.count # output: 3
## binding.pry
end
With pry inserted as shown, the same queries produce:
pry(#<RSpec::Core::ExampleGroup::Nested_1>)> NextAction.all
=> [#<NextAction id: 4, created_at: "2014-04-20 16:53:20", updated_at: "2014-04-20 16:53:20", status: 0>,
#<NextAction id: 5, created_at: "2014-04-20 16:53:20", updated_at: "2014-04-20 16:53:20", status: 0>,
#<NextAction id: 6, created_at: "2014-04-20 16:53:20", updated_at: "2014-04-20 16:53:20", status: 0>]
pry(#<RSpec::Core::ExampleGroup::Nested_1>)> NextActionsOrder.all
=> [#<NextActionsOrder id: 2, next_action_id: 6, order_id: 3>,
#<NextActionsOrder id: 3, next_action_id: 6, order_id: 4>]
pry(#<RSpec::Core::ExampleGroup::Nested_1>)> Order.all
=> [#<Order id: 3, created_at: "2014-04-20 16:53:20", updated_at: "2014-04-20 16:53:20", status: 0>,
#<Order id: 4, created_at: "2014-04-20 16:53:20", updated_at: "2014-04-20 16:53:20", status: 0>]
Again, everything looks good, except now we have 2 orphan NextAction records - IDs #4 and #5.
Any ideas? Thanks so much!
It's because of the next_action in the factory :next_actions_order, which creates an additional next_action...
I would rewrite the next_action factory using
after(:build) do |next_action, evaluator|
next_action.orders << build(:order)
end
or
after(:build) do |next_action, evaluator|
next_action.orders << build_list(:order, evaluator.orders_count)
end
if you need more than one.
This way the entire chain is created at once with all cross references filled in as they should! (and without doubles!)

Rspec new expectation syntax

I've the following rspec unit test:
require 'spec_helper'
describe Article do
describe ".recents" do
it "includes articles created less than one week ago" do
article = Article.create(created_at: Date.today - 1.week + 1.second)
expect(Article.recents).to eql([article])
end
it "excludes articles published at midnight one week ago" do
article = Article.create!(:created_at => Date.today - 1.week)
expect(Article.recents).to be_empty
end
end
end
and the Articlemodel:
class Article < ActiveRecord::Base
attr_accessible :description, :name, :price, :created_at
scope :recents, where('created_at <= ?', 1.week.ago)
end
when I run my tests I get:
1) Article.recents includes articles created less than one week ago
Failure/Error: expect(Article.recents).to eql([article])
expected: [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]
got: [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]
(compared using eql?)
Diff:#<ActiveRecord::Relation:0x007ff692bce158>.==([#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]) returned false even though the diff between #<ActiveRecord::Relation:0x007ff692bce158> and [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>] is empty. Check the implementation of #<ActiveRecord::Relation:0x007ff692bce158>.==.
# ./spec/models/article_spec.rb:7:in `block (3 levels) in <top (required)>'
Could someone please help me to figure out what's the error in my test?
It seems good for me.
You are comparing an activerecord relation (Article.recents) to an array ([article]), which is why the expectation is failing. (It looks like they are the same in the spec results because inspect converts the relation into an array before printing it out.)
Change your first expectation to this:
expect(Article.recents.to_a).to eql([article])

Rails 3.1: Creating objects via .build seems to conflict with validates_presence_of

I am having trouble combining a validation of a foreign key field, and making use of the .build method to create objects. See the classes below.
class Parent < ActiveRecord::Base
belongs_to :family
has_one :family_user
validates_presence_of :name
validates_associated :family
validates_presence_of :family_id
end
class Family < ActiveRecord::Base
belongs_to :organization
has_many :parents
...
end
This generally works fine, but in the app I want to use the build method. For example, the following code in both a spec and in the rails console fails because it is looking for a family_id on the Parent record.
fam = Family.new(:organization_id => 1)
fam.children.build(:name => "Billy Jones")
fam.parents.build(:name => "Mister Jones")
fam.save!
Without validates_presence_of :family_id this code works, and indeed a family_id is properly recorded.
Question: is there a way to validate that a family_id is indeed recorded, while also being able to use .build?
For reference, an error in the console is:
ruby-1.9.2-p290 :082 > fam = Family.new(:organization_id => 1)
=> #<Family id: nil, organization_id: 1, created_at: nil, updated_at: nil, url_token: nil>
ruby-1.9.2-p290 :083 > fam.children.build(:name => "Billy Jones")
=> #<Child id: nil, family_id: nil, name: "Billy Jones", gender: nil, birth_date: nil, desired_start_date: nil, application_date: nil, notes: nil, custom1: nil, custom2: nil, custom3: nil, custom4: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p290 :084 > fam.parents.build(:name => "Mister Jones")
=> #<Parent id: nil, family_id: nil, name: "Mister Jones", address: nil, phone: nil, email: nil, notes: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p290 :085 > fam.save!
ActiveRecord::RecordInvalid: Validation failed: Parents family can't be blank
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/activerecord-3.1.1/lib/active_record/validations.rb:56:in `save!'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/activerecord-3.1.1/lib/active_record/attribute_methods/dirty.rb:33:in `save!'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/activerecord-3.1.1/lib/active_record/transactions.rb:246:in `block in save!'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/activerecord-3.1.1/lib/active_record/transactions.rb:295:in `block in with_transaction_returning_status'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/activerecord-3.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/activerecord-3.1.1/lib/active_record/transactions.rb:208:in `transaction'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/activerecord-3.1.1/lib/active_record/transactions.rb:293:in `with_transaction_returning_status'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/activerecord-3.1.1/lib/active_record/transactions.rb:246:in `save!'
from (irb):85
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/railties-3.1.1/lib/rails/commands/console.rb:45:in `start'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/railties-3.1.1/lib/rails/commands/console.rb:8:in `start'
from /Users/business/.rvm/gems/ruby-1.9.2-p290#rails311/gems/railties-3.1.1/lib/rails/commands.rb:40:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
ruby-1.9.2-p290 :086 >
I would understand it the same way, at least upon initial reading the code that you have.
Maybe you can try this alternate solution:
http://forums.pragprog.com/forums/74/topics/732
Remove
validates_associated :family
validates_presence_of :family_id
and try this
validates_presence_of :family
Hope this helps.

Resources