Testing AttributeList Management and Association Handling in FactoryBot
This test suite validates the AttributeList class functionality in FactoryBot, focusing on attribute management, associations, and filtering capabilities. The tests ensure proper handling of attribute definitions, associations, and list manipulations while maintaining data integrity.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
thoughtbot/factory_bot
spec/factory_bot/attribute_list_spec.rb
module AttributeList
def build_attribute_list(*attributes)
FactoryBot::AttributeList.new.tap do |list|
attributes.each { |attribute| list.define_attribute(attribute) }
end
end
end
describe FactoryBot::AttributeList, "#define_attribute" do
it "maintains a list of attributes" do
attribute = double(:attribute, name: :attribute_name)
another_attribute = double(:attribute, name: :another_attribute_name)
list = FactoryBot::AttributeList.new
list.define_attribute(attribute)
expect(list.to_a).to eq [attribute]
list.define_attribute(another_attribute)
expect(list.to_a).to eq [attribute, another_attribute]
end
it "returns the attribute" do
attribute = double(:attribute, name: :attribute_name)
list = FactoryBot::AttributeList.new
expect(list.define_attribute(attribute)).to eq attribute
end
it "raises if an attribute has already been defined" do
attribute = double(:attribute, name: :attribute_name)
list = FactoryBot::AttributeList.new
expect {
2.times { list.define_attribute(attribute) }
}.to raise_error(
FactoryBot::AttributeDefinitionError,
"Attribute already defined: attribute_name"
)
end
end
describe FactoryBot::AttributeList, "#define_attribute with a named attribute list" do
it "raises when the attribute is a self-referencing association" do
association_with_same_name = FactoryBot::Attribute::Association.new(:author, :author, {})
list = FactoryBot::AttributeList.new(:author)
expect {
list.define_attribute(association_with_same_name)
}.to raise_error(
FactoryBot::AssociationDefinitionError,
"Self-referencing association 'author' in 'author'"
)
end
it "does not raise when the attribute is not a self-referencing association" do
association_with_different_name = FactoryBot::Attribute::Association.new(:author, :post, {})
list = FactoryBot::AttributeList.new
expect { list.define_attribute(association_with_different_name) }.to_not raise_error
end
end
describe FactoryBot::AttributeList, "#apply_attributes" do
include AttributeList
it "adds attributes in the order defined" do
attribute1 = double(:attribute1, name: :attribute1)
attribute2 = double(:attribute2, name: :attribute2)
list = FactoryBot::AttributeList.new
list.define_attribute(attribute1)
list.apply_attributes(build_attribute_list(attribute2))
expect(list.to_a).to eq [attribute1, attribute2]
end
end
describe FactoryBot::AttributeList, "#associations" do
include AttributeList
it "returns associations" do
email_attribute = FactoryBot::Attribute::Dynamic.new(
:email,
false,
->(u) { "#{u.full_name}@example.com" }
)
author_attribute = FactoryBot::Attribute::Association.new(:author, :user, {})
profile_attribute = FactoryBot::Attribute::Association.new(:profile, :profile, {})
list = build_attribute_list(email_attribute, author_attribute, profile_attribute)
expect(list.associations.to_a).to eq [author_attribute, profile_attribute]
end
end
describe FactoryBot::AttributeList, "filter based on ignored attributes" do
include AttributeList
def build_ignored_attribute(name)
FactoryBot::Attribute::Dynamic.new(name, true, -> { "value" })
end
def build_non_ignored_attribute(name)
FactoryBot::Attribute::Dynamic.new(name, false, -> { "value" })
end
it "filters #ignored attributes" do
list = build_attribute_list(
build_ignored_attribute(:comments_count),
build_non_ignored_attribute(:email)
)
expect(list.ignored.names).to eq [:comments_count]
end
it "filters #non_ignored attributes" do
list = build_attribute_list(
build_ignored_attribute(:comments_count),
build_non_ignored_attribute(:email)
)
expect(list.non_ignored.names).to eq [:email]
end
end
describe FactoryBot::AttributeList, "generating names" do
include AttributeList
def build_ignored_attribute(name)
FactoryBot::Attribute::Dynamic.new(name, true, -> { "value" })
end
def build_non_ignored_attribute(name)
FactoryBot::Attribute::Dynamic.new(name, false, -> { "value" })
end
def build_association(name)
FactoryBot::Attribute::Association.new(name, :user, {})
end
it "knows all its #names" do
list = build_attribute_list(
build_ignored_attribute(:comments_count),
build_non_ignored_attribute(:last_name),
build_association(:avatar)
)
expect(list.names).to eq [:comments_count, :last_name, :avatar]
end
it "knows all its #names for #ignored attributes" do
list = build_attribute_list(
build_ignored_attribute(:posts_count),
build_non_ignored_attribute(:last_name),
build_association(:avatar)
)
expect(list.ignored.names).to eq [:posts_count]
end
it "knows all its #names for #non_ignored attributes" do
list = build_attribute_list(
build_ignored_attribute(:posts_count),
build_non_ignored_attribute(:last_name),
build_association(:avatar)
)
expect(list.non_ignored.names).to eq [:last_name, :avatar]
end
it "knows all its #names for #associations" do
list = build_attribute_list(
build_ignored_attribute(:posts_count),
build_non_ignored_attribute(:last_name),
build_association(:avatar)
)
expect(list.associations.names).to eq [:avatar]
end
end