lib/test_prof/factory_doctor.rb
# frozen_string_literal: true require "test_prof/factory_doctor/factory_girl_patch" module TestProf # FactoryDoctor is a tool that helps you identify # tests that perform unnecessary database queries. module FactoryDoctor class Result # :nodoc: attr_reader :count, :time, :queries_count def initialize(count, time, queries_count) @count = count @time = time @queries_count = queries_count end def bad? count.positive? && queries_count.zero? end end IGNORED_QUERIES_PATTERN = %r{( pg_table| pg_attribute| pg_namespace| show\stables| pragma| sqlite_master/rollback| \ATRUNCATE TABLE| \AALTER TABLE| \ABEGIN| \ACOMMIT| \AROLLBACK| \ARELEASE| \ASAVEPOINT )}xi class << self include TestProf::Logging attr_reader :event attr_reader :count, :time, :queries_count # Patch factory lib, init counters def init(event = 'sql.active_record') @event = event reset! log :info, "FactoryDoctor enabled" # Monkey-patch FactoryGirl ::FactoryGirl::FactoryRunner.prepend(FactoryGirlPatch) if defined?(::FactoryGirl) subscribe! end def start reset! @running = true end def stop @running = false end def result Result.new(count, time, queries_count) end # Do not analyze code within the block def ignore @ignored = true res = yield ensure @ignored = false res end def within_factory(strategy) return yield if ignore? || !running? || (strategy != :create) begin ts = TestProf.now if @depth.zero? @depth += 1 @count += 1 yield ensure @depth -= 1 @time += (TestProf.now - ts) if @depth.zero? end end private def reset! @depth = 0 @time = 0.0 @count = 0 @queries_count = 0 end def subscribe! ::ActiveSupport::Notifications.subscribe(event) do |_name, _start, _finish, _id, query| next if ignore? || !running? || within_factory? next if query[:sql] =~ IGNORED_QUERIES_PATTERN @queries_count += 1 end end def within_factory? @depth.positive? end def ignore? @ignored == true end def running? @running == true end end end end require "test_prof/factory_doctor/rspec" if defined?(RSpec::Core) require "test_prof/factory_doctor/minitest" if defined?(Minitest::Reporters) TestProf.activate('FDOC') do TestProf::FactoryDoctor.init end