I\'d like to test that a certain piece of code performs as few SQL queries as possible.
ActiveRecord::TestCase
seems to have its own assert_querie
My vision of Ryan's script (cleaned up a bit and wrapped in a matcher), hope it is still actual for someone:
I put this to spec/support/query_counter.rb
module ActiveRecord
class QueryCounter
attr_reader :query_count
def initialize
@query_count = 0
end
def to_proc
lambda(&method(:callback))
end
def callback(name, start, finish, message_id, values)
@query_count += 1 unless %w(CACHE SCHEMA).include?(values[:name])
end
end
end
and this to spec/support/matchers/exceed_query_limit.rb
RSpec::Matchers.define :exceed_query_limit do |expected|
match do |block|
query_count(&block) > expected
end
failure_message_for_should_not do |actual|
"Expected to run maximum #{expected} queries, got #{@counter.query_count}"
end
def query_count(&block)
@counter = ActiveRecord::QueryCounter.new
ActiveSupport::Notifications.subscribed(@counter.to_proc, 'sql.active_record', &block)
@counter.query_count
end
end
Usage:
expect { MyModel.do_the_queries }.to_not exceed_query_limit(2)
Based on Jaime's answer, the following supports an assertion for the number of queries so far in the current test case, and will log the statements in case of failure. I think it's useful pragmatically to combine a SQL check like this with a functional test as it reduces the setup effort.
class ActiveSupport::TestCase
ActiveSupport::Notifications.subscribe('sql.active_record') do |name, started, finished, unique_id, payload|
(@@queries||=[]) << payload unless payload[:name].in? %w(CACHE SCHEMA)
end
def assert_queries_count(expected_count, message=nil)
assert_equal expected_count, @@queries.size,
message||"Expected #{expected_count} queries, but #{@@queries.size} queries occurred.#{@@queries[0,20].join(' ')}"
end
# common setup in a super-class (or use Minitest::Spec etc to do it another way)
def setup
@@queries = []
end
end
Usage:
def test_something
post = Post.new('foo')
assert_queries_count 1 # SQL performance check
assert_equal "Under construction", post.body # standard functional check
end
Note the query assertion should happen immediately in case the other assertions themselves trigger extra queries.