Replies: 10 comments 2 replies
-
And excerpt from Appaloosa Store blog:
factory :authorization do |auth|
auth.trait :with_user do |auth|
auth.user { create :user }
end
end
/cc @benoittgt |
Beta Was this translation helpful? Give feedback.
-
Yep! I confirm. And we still have some "leaky" factories. By default, factories should not have (in database) association created. |
Beta Was this translation helpful? Give feedback.
-
By detecting some factories' records creation with I found a single test that was really using those dependecies. So I overwrote the factory instance callback method to do nothing like this: FactoryGirl.define do
factory :my_model do
# factory fields
after(:build) do |obj|
def obj.callback_method; end
end
end
end Since we create this |
Beta Was this translation helpful? Give feedback.
-
After checking some specs with And we are just starting with the refactor. I. Literally. Love. You. Pattern we're following: require 'test_prof/any_fixture/dsl'
# Activate AnyFixture DSL (fixture) through refinements
using TestProf::AnyFixture::DSL
RSpec.shared_context 'Shared context for Bla', fixtures: :bla do
before(:all) do
fixture(:foo_f) { create :foo }
fixture(:bar_f) { create :bar }
end
before do
set_factory_default :foo, foo # just this one, the other is not needed as a default
end
let_it_be(:foo) { fixture(:foo_f) }
let_it_be(:bar) { fixture(:bar_f) }
end Currently thinking about just dropping the |
Beta Was this translation helpful? Give feedback.
-
Wow! 🙀 let_it_be(:foo) { fixture(:foo_f) }
let_it_be(:bar) { fixture(:bar_f) } Using Using |
Beta Was this translation helpful? Give feedback.
-
Discourse +
See also discourse/discourse#7414 |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
We've cut our spec run time on my previous project four-fold. Part of the analysis was made with the help of the awesome factory profiler. This research gave birth to a monkey patch @DNNX authored that helped to reuse models created by factories with complex interdependencies. @DNNX would you be up to open source that brilliant and flawless piece of code, and maybe contribute it to At some point, under the pressure of getting specs under a given execution time, |
Beta Was this translation helpful? Give feedback.
-
This is the patch (two files): module Monkey
# This module is an extension to `FactoryBot::FactoryRunner` which provides
# a cached version of the `#run` method.
module FactoryRunnerWithCaching
# `RSpecExtension` module is designed to be prepended to RSpec's config,
# so that if one calls `create` from a spec directly, the `create` call
# would reuse any associated records.
module RSpecExtension
def create(*)
::FactoryBotAssociationCache.run_within { super }
end
def build(*)
::FactoryBotAssociationCache.run_within { super }
end
def build_stubbed(*)
::FactoryBotAssociationCache.run_within { super }
end
end
# Runs a factory - either "creating", "building" or "build_stubbing" a new object.
# Most of the implementation is copied from `FactoryBot::Runner#run`, but
# caching support is added at the end of the method, so that instead of
# going through the regular way of constructing the whole object and its
# associations.
def run(runner_strategy = @strategy, &block)
FactoryBotAssociationCache.instance.fetch(
name: @name,
strategy: runner_strategy,
traits: @traits,
overrides: @overrides
) { super }
end
end
end
::FactoryBot::FactoryRunner.prepend(::Monkey::FactoryRunnerWithCaching) # Caches factory bot objects
class FactoryBotAssociationCache
def self.run_within
instance.enable!
yield
ensure
instance.disable!
end
def self.instance
Thread.current[:factory_bot_association_cache] ||= new
end
def initialize
@cache = {}
@enabled = false
end
def enable!
@enabled = true
end
def disable!
@enabled = false
@cache.clear
end
def enabled?
@enabled
end
# Fetches the factory from the cache by its name, strategy, traits and overrides.
# Only `create` strategy is cached now.
# If a set of `overrides` is a _subset_ of overrides of an object in the cache,
# then this object is considered a match and returned. Example.
#
# ```
# FactoryBotAssociationCache.run_within do
# a = create(:user, full_name: 'A', status: :active) # adds talent to the cache, as well as its associations
# b = create(:user, full_name: 'A') # returns previously cached `a`
# c = create(:user) # returns previously cached `a`
# d = create(:user, :active) # creates a new object in the cache because traits are different
# e = create(:user, full_name: 'E') # creates a new object in the cache
# @param name [Symbol] factory name, e.g. `:user` or `:post`
# @param strategy [Symbol] strategy name, e.g. `:create` or `:build_stubbed`
# @param traits [Array<Symbol>|nil] trait names, e.g. `[:active, :extended]`
# @param overrides [Hash] attribute overrides, e.g. `{full_name: 'John Doe', status: :active}`
# @yieldreturn ActiveRecord object which is cached if there was no match in the cache
# @return ActiveRecord object either from the cache or created by the given block
def fetch(name:, strategy:, traits:, overrides:)
return yield unless enabled?
return yield if traits.include?(:skip_factory_bot_cache)
return yield unless strategy == :create
key = [name, strategy, traits.to_set]
subcache = (@cache[key] ||= {})
_, candidate = subcache.find { |sub_overrides, _| overrides <= sub_overrides }
candidate || (subcache[overrides] = yield)
end
end Not sure if it's suitable for inclusion to |
Beta Was this translation helpful? Give feedback.
-
Coming here to say a massive thankyou for the library and it's awesome documentation 🙇 Just used test-prof at work, on our deployment tooling which is a rails project. Specifically RubyProf and RSpecDissect where super useful. With around 3 hours of playing I got the following results: Before: 287 seconds (4:47) One of the issues highlighted by RubyProf digging was a very inefficient method on the hot path in lots of tests. Outputting the flat wall clock rubyprof output showed this lovely method being called how many times! (this is a single rspec file)
Highly recommend folks take a look at the RubyProf output once they've got wins out of the Let/Before bits. Cool thing, as well as making tests quicker, this improved our response time in production by a huge chunk too at p95. |
Beta Was this translation helpful? Give feedback.
-
This thread (not a an issue at all) aims to collect the stories of detecting and fixing test suites bottlenecks. Not necessary related to TestProf, btw.
That would help others to improve their tests and would help me to make the TestProf gem better.
Please, comment on this issue to share your story!
Beta Was this translation helpful? Give feedback.
All reactions