Validating Factory Linting Implementation in factory_bot
This test suite validates the linting functionality of FactoryBot, focusing on factory validation, trait handling, and strategy implementation. It ensures factories produce valid objects and properly handle validation errors across different configurations.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
thoughtbot/factory_bot
spec/acceptance/lint_spec.rb
describe "FactoryBot.lint" do
it "raises when a factory is invalid" do
define_model "User", name: :string do
validates :name, presence: true
end
define_model "AlwaysValid"
FactoryBot.define do
factory :user do
factory :admin_user
end
factory :always_valid
end
error_message = <<~ERROR_MESSAGE.strip
The following factories are invalid:
* user - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)
* admin_user - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)
ERROR_MESSAGE
expect {
FactoryBot.lint
}.to raise_error FactoryBot::InvalidFactoryError, error_message
end
it "does not raise when all factories are valid" do
define_model "User", name: :string do
validates :name, presence: true
end
FactoryBot.define do
factory :user do
name { "assigned" }
end
end
expect { FactoryBot.lint }.not_to raise_error
end
it "allows for selective linting" do
define_model "InvalidThing", name: :string do
validates :name, presence: true
end
define_model "ValidThing", name: :string
FactoryBot.define do
factory :valid_thing
factory :invalid_thing
end
expect {
only_valid_factories = FactoryBot.factories.reject { |factory|
factory.name =~ /invalid/
}
FactoryBot.lint only_valid_factories
}.not_to raise_error
end
describe "trait validation" do
context "enabled" do
it "raises if a trait produces an invalid object" do
define_model "User", name: :string do
validates :name, presence: true
end
FactoryBot.define do
factory :user do
name { "Yep" }
trait :unnamed do
name { nil }
end
end
end
error_message = <<~ERROR_MESSAGE.strip
The following factories are invalid:
* user+unnamed - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)
ERROR_MESSAGE
expect {
FactoryBot.lint traits: true
}.to raise_error FactoryBot::InvalidFactoryError, error_message
end
it "does not raise if a trait produces a valid object" do
define_model "User", name: :string do
validates :name, presence: true
end
FactoryBot.define do
factory :user do
name { "Yep" }
trait :renamed do
name { "Yessir" }
end
end
end
expect {
FactoryBot.lint traits: true
}.not_to raise_error
end
end
context "disabled" do
it "does not raises if a trait produces an invalid object" do
define_model "User", name: :string do
validates :name, presence: true
end
FactoryBot.define do
factory :user do
name { "Yep" }
trait :unnamed do
name { nil }
end
end
end
expect {
FactoryBot.lint traits: false
FactoryBot.lint
}.not_to raise_error
end
end
end
describe "factory strategy for linting" do
it "uses the requested strategy" do
define_class "User" do
attr_accessor :name
def save!
raise "expected :build strategy, #save! shouldn't be invoked"
end
end
FactoryBot.define do
factory :user do
name { "Barbara" }
end
end
expect {
FactoryBot.lint strategy: :build
}.not_to raise_error
end
it "uses the requested strategy during trait validation" do
define_class "User" do
attr_accessor :name
def save!
raise "expected :build strategy, #save! shouldn't be invoked"
end
end
FactoryBot.define do
factory :user do
name { "Barbara" }
trait :male do
name { "Bob" }
end
end
end
expect {
FactoryBot.lint traits: true, strategy: :build
}.not_to raise_error
end
end
describe "verbose linting" do
it "prints the backtrace for each factory error" do
define_class("InvalidThing") do
def save!
raise "invalid"
end
end
FactoryBot.define do
factory :invalid_thing
end
expect {
FactoryBot.lint(verbose: true)
}.to raise_error(
FactoryBot::InvalidFactoryError,
%r{#{__FILE__}:\d*:in `save!'}
)
end
end
end