Skip to content

Instantly share code, notes, and snippets.

@Dania02525
Created April 13, 2023 20:47
Show Gist options
  • Save Dania02525/149f120bb347c6233aa1bc28d2a7f8cf to your computer and use it in GitHub Desktop.
Save Dania02525/149f120bb347c6233aa1bc28d2a7f8cf to your computer and use it in GitHub Desktop.
A custom rspec matcher to catch n+1 queries
# use this matched to catch most n+1 queries in request specs by creating at least three records, then
# doing something like expect { subject }.to not_execute_n_plus_one
RSpec::Matchers.define :not_execute_n_plus_one do
match do |block|
@queries = []
ActiveSupport::Notifications.subscribe 'sql.active_record' do |_, _, _, _, values|
next if %w(CACHE SCHEMA).include? values[:name]
next if values[:sql].start_with?("SAVEPOINT")
next if values[:sql].start_with?("RELEASE SAVEPOINT")
next if values[:sql].start_with?("INSERT INTO")
@queries << values[:sql]
end
block.call
@sqls = @queries.reduce({}) do |acc, q|
acc["\"#{q}\""] ||= 0
acc["\"#{q}\""] += 1
acc
end.select { |_k, v| v > 2 }
@sqls.empty?
end
supports_block_expectations
failure_message do |_actual|
sql_messages = @sqls.map do |k, v|
"#{k} (executed #{v} times)"
end
"The following n+1(s) queries were found: \n#{sql_messages.join("\n")}"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment