Search code examples
ruby-on-railsruby-on-rails-3rspecrspec2rspec-rails

RSpec: "schema does not exist" when calling a method in one model from a method in another model


Trying to follow the law of demeter, I'm in the process of refactoring code. The goal is for every model to have methods that serve as APIs for other models to extract data.

Organization has an instance method #suppliers_for_purchaser, which returns all suppliers for one purchaser. This method has been successfully tested in organization_spec.rb.

Price has a class method .latest_prices_for_purchaser. This class method takes one argument (an instance of Organization) and uses this argument to call Organization#suppliers_for_purchaser. This causes an error schema does not exist in RSpec:

Failures:

1) Price.latest_prices_for_purchaser returns latest prices for purchaser
 Failure/Error: Price.latest_prices_for_purchaser(purchaser).should have(2).prices
 ActiveRecord::StatementInvalid:
   PG::Error: ERROR:  schema "organization" does not exist
   : SELECT COUNT(*) FROM "organizations" INNER JOIN "partnerships" ON "organizations"."id" = "partnerships"."partner_id" WHERE "partnerships"."organization_id" = 1 AND (organization.organization_role.name = 'supplier')
 # ./spec/models/price_spec.rb:24:in `block (3 levels) in <top (required)>'

Models (simplified)

class Organization < ActiveRecord::Base
  # self referencing n:n table
  has_many :partners, :through => :partnerships
  # some more associations, all tested

  # successfully tested in an RSpec unit test for this model
  def suppliers_for_purchaser    
    partners.where('organization.organization_role.name = ?', "supplier")
  end
end

class Price < ActiveRecord::Base
  def self.latest_prices_for_purchaser(purchaser)
    suppliers = purchaser.suppliers_for_purchaser
    # some more code that doesn't get executed because it crashes on the line above
  end
end

price_spec.rb (simlified)

describe Price do
  describe ".latest_prices_for_purchaser" do

    # passes
    it "responds" do
      Price.should respond_to(:latest_prices_for_purchaser)
    end

    it "returns latest prices for purchaser" do
      purchaser = create(:organization_purchaser)
      supplier = create(:organization_supplier)
  partnership = create(:partnership, organization: purchaser, partner: supplier) 
      2.times do
        price = create(:price, created_at: 10.hours.ago, supplier: supplier, purchaser: purchaser)
      end
      Price.latest_prices_for_purchaser(purchaser).should have(2).prices                  
    end

  end
end

Update

cheeseweasel found the solution. Unit testing Price.latest_prices_for_purchaser only worked when changing Organization#suppliers_for_purchaser to:

partners.joins(:organization_role).where('organization_roles.name = ?', "supplier")

Solution

  • The issue is with the suppliers_for_purchaser method - when constructing queries, you need to explicitely define any joins to related tables, so the following:

    partners.where('organization.organization_role.name = ?', "supplier")
    

    needs to have the organization_role join defined:

    partners.joins(:organization_role).where('organization_roles.name = ?', "supplier")