Counting the number of queries performed

后端 未结 8 1783
[愿得一人]
[愿得一人] 2020-12-08 01:53

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

相关标签:
8条回答
  • 2020-12-08 02:57

    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)
    
    0 讨论(0)
  • 2020-12-08 02:57

    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.

    0 讨论(0)
提交回复
热议问题