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'))
|
@traits = Decorator::DisallowsDuplicatesRegistry.new(Registry.new('Trait'))
|
||||||
@strategies = Registry.new('Strategy')
|
@strategies = Registry.new('Strategy')
|
||||||
@callback_names = Set.new
|
@callback_names = Set.new
|
||||||
@definition = Definition.new
|
@definition = Definition.new(:configuration)
|
||||||
|
|
||||||
@allow_class_lookup = true
|
@allow_class_lookup = true
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
module FactoryBot
|
module FactoryBot
|
||||||
# @api private
|
# @api private
|
||||||
class Definition
|
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)
|
@declarations = DeclarationList.new(name)
|
||||||
@callbacks = []
|
@callbacks = []
|
||||||
@defined_traits = Set.new
|
@defined_traits = Set.new
|
||||||
|
|
|
@ -121,7 +121,9 @@ module FactoryBot
|
||||||
#
|
#
|
||||||
# Except that no globally available sequence will be defined.
|
# Except that no globally available sequence will be defined.
|
||||||
def sequence(name, *args, &block)
|
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) }
|
add_attribute(name) { increment_sequence(sequence) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,4 +20,80 @@ describe "FactoryBot.rewind_sequences" do
|
||||||
expect(email).to eq "somebody1@example.com"
|
expect(email).to eq "somebody1@example.com"
|
||||||
expect(name).to eq "Joe"
|
expect(name).to eq "Joe"
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
describe FactoryBot::DefinitionProxy, "#add_attribute" do
|
describe FactoryBot::DefinitionProxy, "#add_attribute" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
|
|
||||||
it "raises if both a block and value are given" do
|
it "raises if both a block and value are given" do
|
||||||
|
@ -21,7 +21,7 @@ describe FactoryBot::DefinitionProxy, "#add_attribute" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#add_attribute when the proxy ignores attributes" do
|
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) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject, true) }
|
||||||
|
|
||||||
it "raises if both a block and value are given" do
|
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
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#transient" do
|
describe FactoryBot::DefinitionProxy, "#transient" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
|
|
||||||
it "makes all attributes added ignored" do
|
it "makes all attributes added ignored" do
|
||||||
|
@ -56,7 +56,7 @@ describe FactoryBot::DefinitionProxy, "#transient" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#method_missing" do
|
describe FactoryBot::DefinitionProxy, "#method_missing" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
|
|
||||||
it "declares an implicit declaration without args or a block" do
|
it "declares an implicit declaration without args or a block" do
|
||||||
|
@ -82,30 +82,44 @@ describe FactoryBot::DefinitionProxy, "#method_missing" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#sequence" do
|
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
|
it "creates a new sequence starting at 1" do
|
||||||
proxy.sequence(:great)
|
proxy = build_proxy(:factory)
|
||||||
expect(FactoryBot::Sequence).to have_received(:new).with(:great)
|
proxy.sequence(:sequence)
|
||||||
|
|
||||||
|
expect(FactoryBot::Sequence).to have_received(:new).
|
||||||
|
with("__factory_sequence__")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates a new sequence with an overridden starting vaue" do
|
it "creates a new sequence with an overridden starting vaue" do
|
||||||
proxy.sequence(:great, "C")
|
proxy = build_proxy(:factory)
|
||||||
expect(FactoryBot::Sequence).to have_received(:new).with(:great, "C")
|
proxy.sequence(:sequence, "C")
|
||||||
|
|
||||||
|
expect(FactoryBot::Sequence).to have_received(:new).
|
||||||
|
with("__factory_sequence__", "C")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates a new sequence with a block" do
|
it "creates a new sequence with a block" do
|
||||||
sequence_block = Proc.new { |n| "user+#{n}@example.com" }
|
sequence_block = Proc.new { |n| "user+#{n}@example.com" }
|
||||||
proxy.sequence(:great, 1, &sequence_block)
|
proxy = build_proxy(:factory)
|
||||||
expect(FactoryBot::Sequence).to have_received(:new).with(:great, 1, &sequence_block)
|
proxy.sequence(:sequence, 1, &sequence_block)
|
||||||
|
|
||||||
|
expect(FactoryBot::Sequence).to have_received(:new).
|
||||||
|
with("__factory_sequence__", 1, &sequence_block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#association" do
|
describe FactoryBot::DefinitionProxy, "#association" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
|
|
||||||
it "declares an association" do
|
it "declares an association" do
|
||||||
|
@ -120,7 +134,7 @@ describe FactoryBot::DefinitionProxy, "#association" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "adding callbacks" do
|
describe FactoryBot::DefinitionProxy, "adding callbacks" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
let(:callback) { -> { "my awesome callback!" } }
|
let(:callback) { -> { "my awesome callback!" } }
|
||||||
|
|
||||||
|
@ -159,7 +173,7 @@ describe FactoryBot::DefinitionProxy, "adding callbacks" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#to_create" do
|
describe FactoryBot::DefinitionProxy, "#to_create" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
|
|
||||||
it "accepts a block to run in place of #save!" do
|
it "accepts a block to run in place of #save!" do
|
||||||
|
@ -170,7 +184,7 @@ describe FactoryBot::DefinitionProxy, "#to_create" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#factory" do
|
describe FactoryBot::DefinitionProxy, "#factory" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
|
|
||||||
it "without options" do
|
it "without options" do
|
||||||
|
@ -191,7 +205,7 @@ describe FactoryBot::DefinitionProxy, "#factory" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#trait" do
|
describe FactoryBot::DefinitionProxy, "#trait" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
|
|
||||||
it "declares a trait" do
|
it "declares a trait" do
|
||||||
|
@ -202,7 +216,7 @@ describe FactoryBot::DefinitionProxy, "#trait" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::DefinitionProxy, "#initialize_with" do
|
describe FactoryBot::DefinitionProxy, "#initialize_with" do
|
||||||
subject { FactoryBot::Definition.new }
|
subject { FactoryBot::Definition.new(:name) }
|
||||||
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryBot::DefinitionProxy.new(subject) }
|
||||||
|
|
||||||
it "defines the constructor on the definition" do
|
it "defines the constructor on the definition" do
|
||||||
|
|
|
@ -1,19 +1,29 @@
|
||||||
describe FactoryBot::Definition do
|
describe FactoryBot::Definition do
|
||||||
|
subject { described_class.new(:name) }
|
||||||
|
|
||||||
it { should delegate(:declare_attribute).to(:declarations) }
|
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
|
it "creates a new attribute list with the name passed" do
|
||||||
|
name = "great name"
|
||||||
allow(FactoryBot::DeclarationList).to receive(:new)
|
allow(FactoryBot::DeclarationList).to receive(:new)
|
||||||
subject
|
|
||||||
|
FactoryBot::Definition.new(name)
|
||||||
|
|
||||||
expect(FactoryBot::DeclarationList).to have_received(:new).with(name)
|
expect(FactoryBot::DeclarationList).to have_received(:new).with(name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::Definition, "#overridable" do
|
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) }
|
let(:list) { double("declaration list", overridable: true) }
|
||||||
before do
|
before do
|
||||||
allow(FactoryBot::DeclarationList).to receive(:new).and_return list
|
allow(FactoryBot::DeclarationList).to receive(:new).and_return list
|
||||||
|
@ -23,9 +33,9 @@ describe FactoryBot::Definition, "#overridable" do
|
||||||
expect(subject.overridable).to eq subject
|
expect(subject.overridable).to eq subject
|
||||||
expect(list).to have_received(:overridable).once
|
expect(list).to have_received(:overridable).once
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::Definition, "defining traits" do
|
describe "defining traits" do
|
||||||
let(:trait_1) { double("trait") }
|
let(:trait_1) { double("trait") }
|
||||||
let(:trait_2) { double("trait") }
|
let(:trait_2) { double("trait") }
|
||||||
|
|
||||||
|
@ -40,9 +50,9 @@ describe FactoryBot::Definition, "defining traits" do
|
||||||
subject.define_trait(trait_1)
|
subject.define_trait(trait_1)
|
||||||
expect(subject.defined_traits.size).to eq 1
|
expect(subject.defined_traits.size).to eq 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::Definition, "adding callbacks" do
|
describe "adding callbacks" do
|
||||||
let(:callback_1) { "callback1" }
|
let(:callback_1) { "callback1" }
|
||||||
let(:callback_2) { "callback2" }
|
let(:callback_2) { "callback2" }
|
||||||
|
|
||||||
|
@ -51,9 +61,9 @@ describe FactoryBot::Definition, "adding callbacks" do
|
||||||
subject.add_callback(callback_2)
|
subject.add_callback(callback_2)
|
||||||
expect(subject.callbacks).to eq [callback_1, callback_2]
|
expect(subject.callbacks).to eq [callback_1, callback_2]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe FactoryBot::Definition, "#to_create" do
|
describe "#to_create" do
|
||||||
its(:to_create) { should be_nil }
|
its(:to_create) { should be_nil }
|
||||||
|
|
||||||
it "returns the assigned value when given a block" do
|
it "returns the assigned value when given a block" do
|
||||||
|
@ -61,4 +71,5 @@ describe FactoryBot::Definition, "#to_create" do
|
||||||
subject.to_create(&block)
|
subject.to_create(&block)
|
||||||
expect(subject.to_create).to eq block
|
expect(subject.to_create).to eq block
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue