Skip to content

Commit

Permalink
Publish factory_bot.compile_factory notification
Browse files Browse the repository at this point in the history
Related to [comment on #1586][]
---

Publish Active Support Notifications under the
`factory_bot.compile_factory` key.

They payload for that event is a `Hash` with keys:

* `name:` - the name of the Factory
* `class:` - the Ruby class the Factory constructs instances of
* `attributes:` - a `FactoryBot::AttributesList` instance for the available
  attributes
* `traits:` - an Array of `FactoryBot::Trait` instances for the available
  traits

[comment on #1586]: #1586 (comment)
  • Loading branch information
seanpdoyle authored and mike-burns committed Sep 1, 2023
1 parent 6486fce commit c50b664
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 3 deletions.
25 changes: 24 additions & 1 deletion GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -1990,7 +1990,7 @@ ActiveSupport Instrumentation

In order to track what factories are created (and with what build strategy),
`ActiveSupport::Notifications` are included to provide a way to subscribe to
factories being run. One example would be to track factories based on a
factories being compiled and run. One example would be to track factories based on a
threshold of execution time.

```ruby
Expand Down Expand Up @@ -2024,6 +2024,29 @@ config.after(:suite) do
end
```

Another example could involve tracking the attributes and traits that factories are compiled with. If you're using RSpec, you could add `before(:suite)` and `after(:suite)` blocks that subscribe to `factory_bot.compile_factory` notifications:

```ruby
factory_bot_results = {}
config.before(:suite) do
ActiveSupport::Notifications.subscribe("factory_bot.compile_factory") do |name, start, finish, id, payload|
factory_name = payload[:name]
factory_class = payload[:class]
attributes = payload[:attributes]
traits = payload[:traits]
factory_bot_results[factory_class] ||= {}
factory_bot_results[factory_class][factory_name] = {
attributes: attributes.map(&:name)
traits: traits.map(&:name)
}
end
end

config.after(:suite) do
puts factory_bot_results
end
```

Rails Preloaders and RSpec
--------------------------

Expand Down
25 changes: 24 additions & 1 deletion docs/src/activesupport-instrumentation/summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

In order to track what factories are created (and with what build strategy),
`ActiveSupport::Notifications` are included to provide a way to subscribe to
factories being run. One example would be to track factories based on a
factories being compiled and run. One example would be to track factories based on a
threshold of execution time.

```ruby
Expand Down Expand Up @@ -35,3 +35,26 @@ config.after(:suite) do
puts factory_bot_results
end
```

Another example could involve tracking the attributes and traits that factories are compiled with. If you're using RSpec, you could add `before(:suite)` and `after(:suite)` blocks that subscribe to `factory_bot.compile_factory` notifications:

```ruby
factory_bot_results = {}
config.before(:suite) do
ActiveSupport::Notifications.subscribe("factory_bot.compile_factory") do |name, start, finish, id, payload|
factory_name = payload[:name]
factory_class = payload[:class]
attributes = payload[:attributes]
traits = payload[:traits]
factory_bot_results[factory_class] ||= {}
factory_bot_results[factory_class][factory_name] = {
attributes: attributes.map(&:name)
traits: traits.map(&:name)
}
end
end

config.after(:suite) do
puts factory_bot_results
end
```
7 changes: 7 additions & 0 deletions lib/factory_bot/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ def compile(klass = nil)
end

@compiled = true

ActiveSupport::Notifications.instrument "factory_bot.compile_factory", {
name: name,
attributes: declarations.attributes,
traits: defined_traits,
class: klass
}
end
end

Expand Down
44 changes: 43 additions & 1 deletion spec/acceptance/activesupport_instrumentation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def subscribed(callback, *args)
ActiveSupport::Notifications.extend SubscribedBehavior
end

describe "using ActiveSupport::Instrumentation to track factory interaction" do
describe "using ActiveSupport::Instrumentation to track run_factory interaction" do
let(:slow_user_factory) { FactoryBot::Internal.factory_by_name("slow_user") }
let(:user_factory) { FactoryBot::Internal.factory_by_name("user") }
before do
Expand Down Expand Up @@ -78,3 +78,45 @@ def subscribed(callback, *args)
expect(tracked_invocations[:user][:create]).to eq(5)
end
end

describe "using ActiveSupport::Instrumentation to track compile_factory interaction" do
before do
define_model("User", name: :string, email: :string)

FactoryBot.define do
factory :user do
sequence(:email) { |n| "user_#{n}@example.com" }

name { "User" }

trait :special do
name { "Special User" }
end
end
end
end

it "tracks proper time of compiling the factory" do
time_to_execute = 0
callback = ->(_name, start, finish, _id, _payload) { time_to_execute = finish - start }
ActiveSupport::Notifications.subscribed(callback, "factory_bot.compile_factory") do
FactoryBot.build(:user)
end

expect(time_to_execute).to be > 0
end

it "builds the correct payload" do
tracked_payloads = []
callback = ->(_name, _start, _finish, _id, payload) { tracked_payloads << payload }

ActiveSupport::Notifications.subscribed(callback, "factory_bot.compile_factory") do
FactoryBot.build(:user)
end

payload = tracked_payloads.detect { |payload| payload[:name] == :user }
expect(payload[:class]).to eq(User)
expect(payload[:attributes].map(&:name)).to eq([:email, :name])
expect(payload[:traits].map(&:name)).to eq(["special"])
end
end

0 comments on commit c50b664

Please sign in to comment.