mirror of
https://github.com/thoughtbot/factory_bot.git
synced 2022-11-09 11:43:51 -05:00
Register inline sequence to allow for rewinding
This was originally opened as #1078, but this addresses the review comments on that PR. By registering the inline sequences, we allow them to get rewound with `FactoryBot.rewind_sequences`. We register them with `__#{factory_name}_#{sequence_name}__` to avoid conflicting with any reasonably named global sequences, and to hint that we should not be generating values from these sequences directly. Co-authored-by: Damian Le Nouaille <dam@dln.name> Co-authored-by: Damian Galarza <galarza.d@gmail.com>
This commit is contained in:
parent
676fa9d3d9
commit
f1d7ae3cc1
6 changed files with 185 additions and 81 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue