Skip to content

Instantly share code, notes, and snippets.

@pithyless
Last active May 5, 2016 12:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pithyless/19a585657532a7fb0a6b to your computer and use it in GitHub Desktop.
Save pithyless/19a585657532a7fb0a6b to your computer and use it in GitHub Desktop.
Helper to check for N+1 queries in rspec
require 'method_struct'
class CheckForN1Queries < MethodStruct.new(:generator, :operation, :debug)
IGNORED_SQL = [
/^SHOW/, /^EXPLAIN/, /^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/,
/^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/,
/^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/
]
def call
2.times { generator.call }
log("BEFORE:")
old_count = count_total_queries { operation.call }
generator.call
log("AFTER:")
new_count = count_total_queries { operation.call }
[old_count, new_count]
end
private
def log(msg)
puts(["\n", msg, "\n"].join) if debug
end
def count_total_queries(&block)
query_count = 0
callback = proc do |name, start, finish, message_id, values|
next if values[:name] == 'CACHE' || IGNORED_SQL.any? { |r| values[:sql] =~ r }
log(values[:sql])
query_count += 1
end
subscribed(callback, "sql.active_record", &block)
query_count
end
# Backported from Rails 3.2.1
def subscribed(callback, *args, &block)
subscriber = ActiveSupport::Notifications.subscribe(*args, &callback)
yield
ensure
ActiveSupport::Notifications.unsubscribe(subscriber)
end
end
describe PostController do
it 'avoids N+1 queries' do
old_count, new_count = CheckForN1Queries.call(
generator: proc { create_blog_post },
operation: proc { get :index },
debug: false
)
expect(new_count).to eq(old_count)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment