diff --git a/lib/factory_bot/configuration.rb b/lib/factory_bot/configuration.rb index 53d0a8d..967d3e2 100644 --- a/lib/factory_bot/configuration.rb +++ b/lib/factory_bot/configuration.rb @@ -11,7 +11,7 @@ module FactoryBot @traits = Decorator::DisallowsDuplicatesRegistry.new(Registry.new('Trait')) @strategies = Registry.new('Strategy') @callback_names = Set.new - @definition = Definition.new + @definition = Definition.new(:configuration) @allow_class_lookup = true diff --git a/lib/factory_bot/definition.rb b/lib/factory_bot/definition.rb index 48c70a0..b303f26 100644 --- a/lib/factory_bot/definition.rb +++ b/lib/factory_bot/definition.rb @@ -1,9 +1,10 @@ module FactoryBot # @api private class Definition - attr_reader :defined_traits, :declarations + attr_reader :defined_traits, :declarations, :name - def initialize(name = nil, base_traits = []) + def initialize(name, base_traits = []) + @name = name @declarations = DeclarationList.new(name) @callbacks = [] @defined_traits = Set.new diff --git a/lib/factory_bot/definition_proxy.rb b/lib/factory_bot/definition_proxy.rb index 973b6c7..9b76f0a 100644 --- a/lib/factory_bot/definition_proxy.rb +++ b/lib/factory_bot/definition_proxy.rb @@ -121,7 +121,9 @@ module FactoryBot # # Except that no globally available sequence will be defined. def sequence(name, *args, &block) - sequence = Sequence.new(name, *args, &block) + sequence_name = "__#{@definition.name}_#{name}__" + sequence = Sequence.new(sequence_name, *args, &block) + FactoryBot.register_sequence(sequence) add_attribute(name) { increment_sequence(sequence) } end diff --git a/spec/acceptance/sequence_resetting_spec.rb b/spec/acceptance/sequence_resetting_spec.rb index e66742d..75ba403 100644 --- a/spec/acceptance/sequence_resetting_spec.rb +++ b/spec/acceptance/sequence_resetting_spec.rb @@ -20,4 +20,80 @@ describe "FactoryBot.rewind_sequences" do expect(email).to eq "somebody1@example.com" expect(name).to eq "Joe" end + + it "resets inline sequences back to their starting value" do + class User + attr_accessor :email + end + + FactoryBot.define do + factory :user do + sequence(:email) { |n| "somebody#{n}@example.com" } + end + end + + build_list(:user, 2) + + FactoryBot.rewind_sequences + + user = build(:user) + + expect(user.email).to eq "somebody1@example.com" + end + + it "does not collide with globally registered factories" do + class User + attr_accessor :email + end + + FactoryBot.define do + sequence(:email) { |n| "global-somebody#{n}@example.com" } + + factory :user do + sequence(:email) { |n| "local-somebody#{n}@example.com" } + end + end + + 2.times do + generate(:email) + end + + build_list(:user, 2) + + FactoryBot.rewind_sequences + + user = build(:user) + email = generate(:email) + + expect(user.email).to eq "local-somebody1@example.com" + expect(email).to eq "global-somebody1@example.com" + end + + it "still allows global sequences prefixed with a factory name" do + class User + attr_accessor :email + end + + FactoryBot.define do + sequence(:user_email) { |n| "global-somebody#{n}@example.com" } + + factory :user do + sequence(:email) { |n| "local-somebody#{n}@example.com" } + end + end + + 2.times do + generate(:user_email) + end + + build_list(:user, 2) + + FactoryBot.rewind_sequences + + user = build(:user) + email = generate(:user_email) + + expect(user.email).to eq "local-somebody1@example.com" + expect(email).to eq "global-somebody1@example.com" + end end diff --git a/spec/factory_bot/definition_proxy_spec.rb b/spec/factory_bot/definition_proxy_spec.rb index 64d90b1..e535bd5 100644 --- a/spec/factory_bot/definition_proxy_spec.rb +++ b/spec/factory_bot/definition_proxy_spec.rb @@ -1,5 +1,5 @@ describe FactoryBot::DefinitionProxy, "#add_attribute" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } it "raises if both a block and value are given" do @@ -21,7 +21,7 @@ describe FactoryBot::DefinitionProxy, "#add_attribute" do end describe FactoryBot::DefinitionProxy, "#add_attribute when the proxy ignores attributes" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject, true) } it "raises if both a block and value are given" do @@ -43,7 +43,7 @@ describe FactoryBot::DefinitionProxy, "#add_attribute when the proxy ignores att end describe FactoryBot::DefinitionProxy, "#transient" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } it "makes all attributes added ignored" do @@ -56,7 +56,7 @@ describe FactoryBot::DefinitionProxy, "#transient" do end describe FactoryBot::DefinitionProxy, "#method_missing" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } it "declares an implicit declaration without args or a block" do @@ -82,30 +82,44 @@ describe FactoryBot::DefinitionProxy, "#method_missing" do end describe FactoryBot::DefinitionProxy, "#sequence" do - subject { FactoryBot::Definition.new } - let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } - before { allow(FactoryBot::Sequence).to receive(:new) } + before do + allow(FactoryBot::Sequence).to receive(:new).and_call_original + end + + def build_proxy(factory_name) + definition = FactoryBot::Definition.new(factory_name) + FactoryBot::DefinitionProxy.new(definition) + end it "creates a new sequence starting at 1" do - proxy.sequence(:great) - expect(FactoryBot::Sequence).to have_received(:new).with(:great) + proxy = build_proxy(:factory) + proxy.sequence(:sequence) + + expect(FactoryBot::Sequence).to have_received(:new). + with("__factory_sequence__") end it "creates a new sequence with an overridden starting vaue" do - proxy.sequence(:great, "C") - expect(FactoryBot::Sequence).to have_received(:new).with(:great, "C") + proxy = build_proxy(:factory) + proxy.sequence(:sequence, "C") + + expect(FactoryBot::Sequence).to have_received(:new). + with("__factory_sequence__", "C") end it "creates a new sequence with a block" do sequence_block = Proc.new { |n| "user+#{n}@example.com" } - proxy.sequence(:great, 1, &sequence_block) - expect(FactoryBot::Sequence).to have_received(:new).with(:great, 1, &sequence_block) + proxy = build_proxy(:factory) + proxy.sequence(:sequence, 1, &sequence_block) + + expect(FactoryBot::Sequence).to have_received(:new). + with("__factory_sequence__", 1, &sequence_block) end end describe FactoryBot::DefinitionProxy, "#association" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } it "declares an association" do @@ -120,7 +134,7 @@ describe FactoryBot::DefinitionProxy, "#association" do end describe FactoryBot::DefinitionProxy, "adding callbacks" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } let(:callback) { -> { "my awesome callback!" } } @@ -159,7 +173,7 @@ describe FactoryBot::DefinitionProxy, "adding callbacks" do end describe FactoryBot::DefinitionProxy, "#to_create" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } it "accepts a block to run in place of #save!" do @@ -170,7 +184,7 @@ describe FactoryBot::DefinitionProxy, "#to_create" do end describe FactoryBot::DefinitionProxy, "#factory" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } it "without options" do @@ -191,7 +205,7 @@ describe FactoryBot::DefinitionProxy, "#factory" do end describe FactoryBot::DefinitionProxy, "#trait" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } it "declares a trait" do @@ -202,7 +216,7 @@ describe FactoryBot::DefinitionProxy, "#trait" do end describe FactoryBot::DefinitionProxy, "#initialize_with" do - subject { FactoryBot::Definition.new } + subject { FactoryBot::Definition.new(:name) } let(:proxy) { FactoryBot::DefinitionProxy.new(subject) } it "defines the constructor on the definition" do diff --git a/spec/factory_bot/definition_spec.rb b/spec/factory_bot/definition_spec.rb index 2238388..6bc4397 100644 --- a/spec/factory_bot/definition_spec.rb +++ b/spec/factory_bot/definition_spec.rb @@ -1,64 +1,75 @@ describe FactoryBot::Definition do + subject { described_class.new(:name) } + it { should delegate(:declare_attribute).to(:declarations) } -end -describe FactoryBot::Definition, "with a name" do - let(:name) { :"great name" } - subject { FactoryBot::Definition.new(name) } + describe "with a name" do + it "creates a new attribute list with the name passed" do + name = "great name" + allow(FactoryBot::DeclarationList).to receive(:new) - it "creates a new attribute list with the name passed" do - allow(FactoryBot::DeclarationList).to receive(:new) - subject - expect(FactoryBot::DeclarationList).to have_received(:new).with(name) - end -end - -describe FactoryBot::Definition, "#overridable" do - let(:list) { double("declaration list", overridable: true) } - before do - allow(FactoryBot::DeclarationList).to receive(:new).and_return list - end - - it "sets the declaration list as overridable" do - expect(subject.overridable).to eq subject - expect(list).to have_received(:overridable).once - end -end - -describe FactoryBot::Definition, "defining traits" do - let(:trait_1) { double("trait") } - let(:trait_2) { double("trait") } - - it "maintains a list of traits" do - subject.define_trait(trait_1) - subject.define_trait(trait_2) - expect(subject.defined_traits).to include(trait_1, trait_2) - end - - it "adds only unique traits" do - subject.define_trait(trait_1) - subject.define_trait(trait_1) - expect(subject.defined_traits.size).to eq 1 - end -end - -describe FactoryBot::Definition, "adding callbacks" do - let(:callback_1) { "callback1" } - let(:callback_2) { "callback2" } - - it "maintains a list of callbacks" do - subject.add_callback(callback_1) - subject.add_callback(callback_2) - expect(subject.callbacks).to eq [callback_1, callback_2] - end -end - -describe FactoryBot::Definition, "#to_create" do - its(:to_create) { should be_nil } - - it "returns the assigned value when given a block" do - block = proc { nil } - subject.to_create(&block) - expect(subject.to_create).to eq block + FactoryBot::Definition.new(name) + + expect(FactoryBot::DeclarationList).to have_received(:new).with(name) + end + end + + describe "#name" do + it "returns the name" do + name = "factory name" + definition = described_class.new(name) + + expect(definition.name).to eq(name) + end + end + + describe "#overridable" do + let(:list) { double("declaration list", overridable: true) } + before do + allow(FactoryBot::DeclarationList).to receive(:new).and_return list + end + + it "sets the declaration list as overridable" do + expect(subject.overridable).to eq subject + expect(list).to have_received(:overridable).once + end + end + + describe "defining traits" do + let(:trait_1) { double("trait") } + let(:trait_2) { double("trait") } + + it "maintains a list of traits" do + subject.define_trait(trait_1) + subject.define_trait(trait_2) + expect(subject.defined_traits).to include(trait_1, trait_2) + end + + it "adds only unique traits" do + subject.define_trait(trait_1) + subject.define_trait(trait_1) + expect(subject.defined_traits.size).to eq 1 + end + end + + describe "adding callbacks" do + let(:callback_1) { "callback1" } + let(:callback_2) { "callback2" } + + it "maintains a list of callbacks" do + subject.add_callback(callback_1) + subject.add_callback(callback_2) + expect(subject.callbacks).to eq [callback_1, callback_2] + end + end + + describe "#to_create" do + its(:to_create) { should be_nil } + + it "returns the assigned value when given a block" do + block = proc { nil } + subject.to_create(&block) + expect(subject.to_create).to eq block + end end end