mirror of
https://github.com/thoughtbot/shoulda-matchers.git
synced 2022-11-09 12:01:38 -05:00
Pass correct object type to association reflection (#992)
It's currently not possible to test a `has_many` association that is defined with a scope block where the block takes an instance of the association source. The `have_many` matcher attempts to call this scope and when it does so it does not pass that instance. This commit fixes that. Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
This commit is contained in:
parent
2af0958575
commit
421a49d0b6
4 changed files with 83 additions and 17 deletions
|
@ -35,12 +35,12 @@ module Shoulda
|
|||
join_table_name.to_s
|
||||
end
|
||||
|
||||
def association_relation
|
||||
def association_relation(related_instance)
|
||||
relation = associated_class.all
|
||||
|
||||
if reflection.scope
|
||||
# Source: AR::Associations::AssociationScope#eval_scope
|
||||
relation.instance_exec(subject, &reflection.scope)
|
||||
relation.instance_exec(related_instance, &reflection.scope)
|
||||
else
|
||||
relation
|
||||
end
|
||||
|
|
|
@ -7,7 +7,6 @@ module Shoulda
|
|||
delegate(
|
||||
:associated_class,
|
||||
:association_foreign_key,
|
||||
:association_relation,
|
||||
:foreign_key,
|
||||
:has_and_belongs_to_many_name,
|
||||
:join_table_name,
|
||||
|
@ -27,6 +26,10 @@ module Shoulda
|
|||
@name = name
|
||||
end
|
||||
|
||||
def association_relation
|
||||
reflection.association_relation(subject)
|
||||
end
|
||||
|
||||
def reflection
|
||||
@reflection ||= reflect_on_association(name)
|
||||
end
|
||||
|
|
|
@ -1004,22 +1004,68 @@ Expected Parent to have a has_many association called children through conceptio
|
|||
expect(matcher.failure_message).to match(/children should be ordered by id/)
|
||||
end
|
||||
|
||||
it 'accepts an association with a valid :conditions option' do
|
||||
define_model :child, parent_id: :integer, adopted: :boolean
|
||||
define_model(:parent).tap do |model|
|
||||
define_association_with_conditions(model, :has_many, :children, adopted: true)
|
||||
context 'if the association has a scope block' do
|
||||
context 'and the block does not take an argument' do
|
||||
context 'and the matcher is given conditions that match the conditions used in the scope' do
|
||||
it 'matches' do
|
||||
define_model :Child, parent_id: :integer, adopted: :boolean
|
||||
define_model(:Parent) do
|
||||
has_many :children, -> { where(adopted: true) }
|
||||
end
|
||||
|
||||
expect(Parent.new).
|
||||
to have_many(:children).
|
||||
conditions(adopted: true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'and the matcher is given conditions that do not match the conditions used in the scope' do
|
||||
it 'rejects an association with a bad :conditions option' do
|
||||
define_model :Child, parent_id: :integer, adopted: :boolean
|
||||
define_model :Parent do
|
||||
has_many :children
|
||||
end
|
||||
|
||||
expect(Parent.new).
|
||||
not_to have_many(:children).
|
||||
conditions(adopted: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
expect(Parent.new).to have_many(:children).conditions(adopted: true)
|
||||
end
|
||||
context 'and the block takes an argument' do
|
||||
context 'and the matcher is given conditions that match the scope' do
|
||||
it 'matches' do
|
||||
define_model :Wheel, bike_id: :integer, tire_id: :integer
|
||||
define_model :Tire
|
||||
define_model :Bike, default_tire_id: :integer do
|
||||
has_many :wheels, -> (bike) do
|
||||
where(tire_id: bike.default_tire_id)
|
||||
end
|
||||
belongs_to :default_tire, class_name: 'Tire'
|
||||
end
|
||||
|
||||
it 'rejects an association with a bad :conditions option' do
|
||||
define_model :child, parent_id: :integer, adopted: :boolean
|
||||
define_model :parent do
|
||||
has_many :children
|
||||
expect(Bike.new(default_tire_id: 42)).
|
||||
to have_many(:wheels).conditions(tire_id: 42)
|
||||
end
|
||||
end
|
||||
|
||||
context 'and the matcher is given conditions that do not match the scope' do
|
||||
it 'matches' do
|
||||
define_model :Wheel, bike_id: :integer, tire_id: :integer
|
||||
define_model :Tire
|
||||
define_model :Bike, default_tire_id: :integer do
|
||||
has_many :wheels, -> (bike) do
|
||||
where(tire_id: bike.default_tire_id)
|
||||
end
|
||||
belongs_to :default_tire, class_name: 'Tire'
|
||||
end
|
||||
|
||||
expect(Bike.new(default_tire_id: 42)).
|
||||
not_to have_many(:wheels).conditions(tire_id: 10)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
expect(Parent.new).not_to have_many(:children).conditions(adopted: true)
|
||||
end
|
||||
|
||||
it 'accepts an association without a :class_name option' do
|
||||
|
|
|
@ -95,12 +95,28 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatchers::ModelReflection d
|
|||
belongs_to :country, -> { where(mood: 'nice') }
|
||||
end
|
||||
delegate_reflection = person_model.reflect_on_association(:country)
|
||||
person = person_model.new
|
||||
reflection = described_class.new(delegate_reflection)
|
||||
|
||||
actual_sql = reflection.association_relation.to_sql
|
||||
actual_sql = reflection.association_relation(person).to_sql
|
||||
expected_sql = Country.where(mood: 'nice').to_sql
|
||||
expect(actual_sql).to eq expected_sql
|
||||
end
|
||||
|
||||
it 'passes the object that has the association to the block' do
|
||||
spy = spy('spy')
|
||||
define_model(:country)
|
||||
person_model = define_model(:person, country_id: :integer) do
|
||||
belongs_to :country, -> (person) { spy.receive(person) }
|
||||
end
|
||||
delegate_reflection = person_model.reflect_on_association(:country)
|
||||
person = person_model.new
|
||||
reflection = described_class.new(delegate_reflection)
|
||||
|
||||
reflection.association_relation(person)
|
||||
|
||||
expect(spy).to have_received(:receive).with(person)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the scope is nil' do
|
||||
|
@ -110,9 +126,10 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatchers::ModelReflection d
|
|||
belongs_to :country
|
||||
end
|
||||
delegate_reflection = person_model.reflect_on_association(:country)
|
||||
person = person_model.new
|
||||
reflection = described_class.new(delegate_reflection)
|
||||
|
||||
actual_sql = reflection.association_relation.to_sql
|
||||
actual_sql = reflection.association_relation(person).to_sql
|
||||
expected_sql = Country.all.to_sql
|
||||
expect(actual_sql).to eq expected_sql
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue